diff options
Diffstat (limited to 'src/core/hle/kernel')
-rw-r--r-- | src/core/hle/kernel/address_arbiter.cpp | 21 | ||||
-rw-r--r-- | src/core/hle/kernel/address_arbiter.h | 4 | ||||
-rw-r--r-- | src/core/hle/kernel/archive.cpp | 436 | ||||
-rw-r--r-- | src/core/hle/kernel/archive.h | 70 | ||||
-rw-r--r-- | src/core/hle/kernel/event.cpp | 47 | ||||
-rw-r--r-- | src/core/hle/kernel/event.h | 14 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.cpp | 29 | ||||
-rw-r--r-- | src/core/hle/kernel/kernel.h | 83 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.cpp | 124 | ||||
-rw-r--r-- | src/core/hle/kernel/mutex.h | 11 | ||||
-rw-r--r-- | src/core/hle/kernel/semaphore.cpp | 94 | ||||
-rw-r--r-- | src/core/hle/kernel/semaphore.h | 32 | ||||
-rw-r--r-- | src/core/hle/kernel/session.h | 58 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.cpp | 53 | ||||
-rw-r--r-- | src/core/hle/kernel/shared_memory.h | 7 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.cpp | 215 | ||||
-rw-r--r-- | src/core/hle/kernel/thread.h | 28 |
17 files changed, 505 insertions, 821 deletions
diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 2b21657da..9a921108d 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -24,23 +24,12 @@ public: Kernel::HandleType GetHandleType() const override { return HandleType::AddressArbiter; } std::string name; ///< Name of address arbiter object (optional) - - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return 0; - } }; //////////////////////////////////////////////////////////////////////////////////////////////////// /// Arbitrate an address -Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { +ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { switch (type) { // Signal thread(s) waiting for arbitrate address... @@ -58,16 +47,16 @@ Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 va // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, handle); + Kernel::WaitCurrentThread(WAITTYPE_ARB, handle, address); HLE::Reschedule(__func__); } break; default: - ERROR_LOG(KERNEL, "unknown type=%d", type); - return -1; + LOG_ERROR(Kernel, "unknown type=%d", type); + return ResultCode(ErrorDescription::InvalidEnumValue, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Usage); } - return 0; + return RESULT_SUCCESS; } /// Create an address arbiter diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index a483fe466..8a5fb10b4 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -11,7 +11,7 @@ // Address arbiters are an underlying kernel synchronization object that can be created/used via // supervisor calls (SVCs). They function as sort of a global lock. Typically, games/other CTR // applications use them as an underlying mechanism to implement thread-safe barriers, events, and -// semphores. +// semphores. //////////////////////////////////////////////////////////////////////////////////////////////////// // Kernel namespace @@ -28,7 +28,7 @@ enum class ArbitrationType : u32 { }; /// Arbitrate an address -Result ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); +ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); /// Create an address arbiter Handle CreateAddressArbiter(const std::string& name = "Unknown"); diff --git a/src/core/hle/kernel/archive.cpp b/src/core/hle/kernel/archive.cpp deleted file mode 100644 index 764082d71..000000000 --- a/src/core/hle/kernel/archive.cpp +++ /dev/null @@ -1,436 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include "common/common_types.h" -#include "common/file_util.h" -#include "common/math_util.h" - -#include "core/file_sys/archive.h" -#include "core/file_sys/archive_sdmc.h" -#include "core/file_sys/directory.h" -#include "core/hle/service/service.h" -#include "core/hle/kernel/archive.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -// Command to access archive file -enum class FileCommand : u32 { - Dummy1 = 0x000100C6, - Control = 0x040100C4, - OpenSubFile = 0x08010100, - Read = 0x080200C2, - Write = 0x08030102, - GetSize = 0x08040000, - SetSize = 0x08050080, - GetAttributes = 0x08060000, - SetAttributes = 0x08070040, - Close = 0x08080000, - Flush = 0x08090000, -}; - -// Command to access directory -enum class DirectoryCommand : u32 { - Dummy1 = 0x000100C6, - Control = 0x040100C4, - Read = 0x08010042, - Close = 0x08020000, -}; - -class Archive : public Object { -public: - std::string GetTypeName() const override { return "Archive"; } - std::string GetName() const override { return name; } - - static Kernel::HandleType GetStaticHandleType() { return HandleType::Archive; } - Kernel::HandleType GetHandleType() const override { return HandleType::Archive; } - - std::string name; ///< Name of archive (optional) - FileSys::Archive* backend; ///< Archive backend interface - - /** - * Synchronize kernel object - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result SyncRequest(bool* wait) override { - u32* cmd_buff = Service::GetCommandBuffer(); - FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); - - switch (cmd) { - // Read from archive... - case FileCommand::Read: - { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - u32 length = cmd_buff[3]; - u32 address = cmd_buff[5]; - - // Number of bytes read - cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); - break; - } - // Write to archive... - case FileCommand::Write: - { - u64 offset = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - - // Number of bytes written - cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); - break; - } - case FileCommand::GetSize: - { - u64 filesize = (u64) backend->GetSize(); - cmd_buff[2] = (u32) filesize; // Lower word - cmd_buff[3] = (u32) (filesize >> 32); // Upper word - break; - } - case FileCommand::SetSize: - { - backend->SetSize(cmd_buff[1] | ((u64)cmd_buff[2] << 32)); - break; - } - case FileCommand::Close: - { - DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<Archive>(GetHandle()); - CloseArchive(backend->GetIdCode()); - break; - } - // Unknown command... - default: - { - ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); - return -1; - } - } - cmd_buff[1] = 0; // No error - return 0; - } - - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return 0; - } -}; - -class File : public Object { -public: - std::string GetTypeName() const override { return "File"; } - std::string GetName() const override { return path; } - - static Kernel::HandleType GetStaticHandleType() { return HandleType::File; } - Kernel::HandleType GetHandleType() const override { return HandleType::File; } - - std::string path; ///< Path of the file - std::unique_ptr<FileSys::File> backend; ///< File backend interface - - /** - * Synchronize kernel object - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result SyncRequest(bool* wait) override { - u32* cmd_buff = Service::GetCommandBuffer(); - FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); - switch (cmd) { - - // Read from file... - case FileCommand::Read: - { - u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 address = cmd_buff[5]; - DEBUG_LOG(KERNEL, "Read %s %s: offset=0x%llx length=%d address=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address); - cmd_buff[2] = backend->Read(offset, length, Memory::GetPointer(address)); - break; - } - - // Write to file... - case FileCommand::Write: - { - u64 offset = cmd_buff[1] | ((u64) cmd_buff[2]) << 32; - u32 length = cmd_buff[3]; - u32 flush = cmd_buff[4]; - u32 address = cmd_buff[6]; - DEBUG_LOG(KERNEL, "Write %s %s: offset=0x%llx length=%d address=0x%x, flush=0x%x", - GetTypeName().c_str(), GetName().c_str(), offset, length, address, flush); - cmd_buff[2] = backend->Write(offset, length, flush, Memory::GetPointer(address)); - break; - } - - case FileCommand::GetSize: - { - DEBUG_LOG(KERNEL, "GetSize %s %s", GetTypeName().c_str(), GetName().c_str()); - u64 size = backend->GetSize(); - cmd_buff[2] = (u32)size; - cmd_buff[3] = size >> 32; - break; - } - - case FileCommand::SetSize: - { - u64 size = cmd_buff[1] | ((u64)cmd_buff[2] << 32); - DEBUG_LOG(KERNEL, "SetSize %s %s size=%llu", GetTypeName().c_str(), GetName().c_str(), size); - backend->SetSize(size); - break; - } - - case FileCommand::Close: - { - DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<File>(GetHandle()); - break; - } - - // Unknown command... - default: - ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); - cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that. - return -1; - } - cmd_buff[1] = 0; // No error - return 0; - } - - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return 0; - } -}; - -class Directory : public Object { -public: - std::string GetTypeName() const override { return "Directory"; } - std::string GetName() const override { return path; } - - static Kernel::HandleType GetStaticHandleType() { return HandleType::Directory; } - Kernel::HandleType GetHandleType() const override { return HandleType::Directory; } - - std::string path; ///< Path of the directory - std::unique_ptr<FileSys::Directory> backend; ///< File backend interface - - /** - * Synchronize kernel object - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result SyncRequest(bool* wait) override { - u32* cmd_buff = Service::GetCommandBuffer(); - DirectoryCommand cmd = static_cast<DirectoryCommand>(cmd_buff[0]); - switch (cmd) { - - // Read from directory... - case DirectoryCommand::Read: - { - u32 count = cmd_buff[1]; - u32 address = cmd_buff[3]; - FileSys::Entry* entries = reinterpret_cast<FileSys::Entry*>(Memory::GetPointer(address)); - DEBUG_LOG(KERNEL, "Read %s %s: count=%d", GetTypeName().c_str(), GetName().c_str(), count); - - // Number of entries actually read - cmd_buff[2] = backend->Read(count, entries); - break; - } - - case DirectoryCommand::Close: - { - DEBUG_LOG(KERNEL, "Close %s %s", GetTypeName().c_str(), GetName().c_str()); - Kernel::g_object_pool.Destroy<Directory>(GetHandle()); - break; - } - - // Unknown command... - default: - ERROR_LOG(KERNEL, "Unknown command=0x%08X!", cmd); - cmd_buff[1] = -1; // TODO(Link Mauve): use the correct error code for that. - return -1; - } - cmd_buff[1] = 0; // No error - return 0; - } - - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return 0; - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -std::map<FileSys::Archive::IdCode, Handle> g_archive_map; ///< Map of file archives by IdCode - -/** - * Opens an archive - * @param id_code IdCode of the archive to open - * @return Handle to archive if it exists, otherwise a null handle (0) - */ -Handle OpenArchive(FileSys::Archive::IdCode id_code) { - auto itr = g_archive_map.find(id_code); - if (itr == g_archive_map.end()) { - return 0; - } - return itr->second; -} - -/** - * Closes an archive - * @param id_code IdCode of the archive to open - * @return Result of operation, 0 on success, otherwise error code - */ -Result CloseArchive(FileSys::Archive::IdCode id_code) { - if (1 != g_archive_map.erase(id_code)) { - ERROR_LOG(KERNEL, "Cannot close archive %d", (int) id_code); - return -1; - } - - INFO_LOG(KERNEL, "Closed archive %d", (int) id_code); - return 0; -} - -/** - * Mounts an archive - * @param archive Pointer to the archive to mount - * @return Result of operation, 0 on success, otherwise error code - */ -Result MountArchive(Archive* archive) { - FileSys::Archive::IdCode id_code = archive->backend->GetIdCode(); - if (0 != OpenArchive(id_code)) { - ERROR_LOG(KERNEL, "Cannot mount two archives with the same ID code! (%d)", (int) id_code); - return -1; - } - g_archive_map[id_code] = archive->GetHandle(); - INFO_LOG(KERNEL, "Mounted archive %s", archive->GetName().c_str()); - return 0; -} - -/** - * Creates an Archive - * @param handle Handle to newly created archive object - * @param backend File system backend interface to the archive - * @param name Optional name of Archive - * @return Newly created Archive object - */ -Archive* CreateArchive(Handle& handle, FileSys::Archive* backend, const std::string& name) { - Archive* archive = new Archive; - handle = Kernel::g_object_pool.Create(archive); - archive->name = name; - archive->backend = backend; - - MountArchive(archive); - - return archive; -} - -/** - * Creates an Archive - * @param backend File system backend interface to the archive - * @param name Optional name of Archive - * @return Handle to newly created Archive object - */ -Handle CreateArchive(FileSys::Archive* backend, const std::string& name) { - Handle handle; - CreateArchive(handle, backend, name); - return handle; -} - -/** - * Open a File from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the File inside of the Archive - * @param mode Mode under which to open the File - * @return Opened File object - */ -Handle OpenFileFromArchive(Handle archive_handle, const std::string& path, const FileSys::Mode mode) { - File* file = new File; - Handle handle = Kernel::g_object_pool.Create(file); - - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - file->path = path; - file->backend = archive->backend->OpenFile(path, mode); - - if (!file->backend) - return 0; - - return handle; -} - -/** - * Create a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Opened Directory object - */ -Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& path) { - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - if (archive == nullptr) - return -1; - if (archive->backend->CreateDirectory(path)) - return 0; - return -1; -} - -/** - * Open a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Opened Directory object - */ -Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& path) { - Directory* directory = new Directory; - Handle handle = Kernel::g_object_pool.Create(directory); - - Archive* archive = Kernel::g_object_pool.GetFast<Archive>(archive_handle); - directory->path = path; - directory->backend = archive->backend->OpenDirectory(path); - - return handle; -} - -/// Initialize archives -void ArchiveInit() { - g_archive_map.clear(); - - // TODO(Link Mauve): Add the other archive types (see here for the known types: - // http://3dbrew.org/wiki/FS:OpenArchive#Archive_idcodes). Currently the only half-finished - // archive type is SDMC, so it is the only one getting exposed. - - std::string sdmc_directory = FileUtil::GetUserPath(D_SDMC_IDX); - auto archive = new FileSys::Archive_SDMC(sdmc_directory); - if (archive->Initialize()) - CreateArchive(archive, "SDMC"); - else - ERROR_LOG(KERNEL, "Can't instantiate SDMC archive with path %s", sdmc_directory.c_str()); -} - -/// Shutdown archives -void ArchiveShutdown() { - g_archive_map.clear(); -} - -} // namespace Kernel diff --git a/src/core/hle/kernel/archive.h b/src/core/hle/kernel/archive.h deleted file mode 100644 index 0230996b6..000000000 --- a/src/core/hle/kernel/archive.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include "common/common_types.h" - -#include "core/hle/kernel/kernel.h" -#include "core/file_sys/archive.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Kernel namespace - -namespace Kernel { - -/** - * Opens an archive - * @param id_code IdCode of the archive to open - * @return Handle to archive if it exists, otherwise a null handle (0) - */ -Handle OpenArchive(FileSys::Archive::IdCode id_code); - -/** - * Closes an archive - * @param id_code IdCode of the archive to open - * @return true if it worked fine - */ -Result CloseArchive(FileSys::Archive::IdCode id_code); - -/** - * Creates an Archive - * @param backend File system backend interface to the archive - * @param name Optional name of Archive - * @return Handle to newly created Archive object - */ -Handle CreateArchive(FileSys::Archive* backend, const std::string& name); - -/** - * Open a File from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the File inside of the Archive - * @param mode Mode under which to open the File - * @return Opened File object - */ -Handle OpenFileFromArchive(Handle archive_handle, const std::string& name, const FileSys::Mode mode); - -/** - * Create a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Whether creation of directory succeeded - */ -Result CreateDirectoryFromArchive(Handle archive_handle, const std::string& name); - -/** - * Open a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Opened Directory object - */ -Handle OpenDirectoryFromArchive(Handle archive_handle, const std::string& name); - -/// Initialize archives -void ArchiveInit(); - -/// Shutdown archives -void ArchiveShutdown(); - -} // namespace FileSys diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 45ed79be8..288080209 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #include <map> #include <algorithm> @@ -30,13 +30,8 @@ public: std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event std::string name; ///< Name of event (optional) - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - *wait = locked; + ResultVal<bool> WaitSynchronization() override { + bool wait = locked; if (locked) { Handle thread = GetCurrentThreadHandle(); if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { @@ -47,7 +42,7 @@ public: if (reset_type != RESETTYPE_STICKY && !permanent_locked) { locked = true; } - return 0; + return MakeResult<bool>(wait); } }; @@ -57,12 +52,12 @@ public: * @param permanent_locked Boolean permanent locked value to set event * @return Result of operation, 0 on success, otherwise error code */ -Result SetPermanentLock(Handle handle, const bool permanent_locked) { - Event* evt = g_object_pool.GetFast<Event>(handle); - _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); +ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { + Event* evt = g_object_pool.Get<Event>(handle); + if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); evt->permanent_locked = permanent_locked; - return 0; + return RESULT_SUCCESS; } /** @@ -71,14 +66,14 @@ Result SetPermanentLock(Handle handle, const bool permanent_locked) { * @param locked Boolean locked value to set event * @return Result of operation, 0 on success, otherwise error code */ -Result SetEventLocked(const Handle handle, const bool locked) { - Event* evt = g_object_pool.GetFast<Event>(handle); - _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); +ResultCode SetEventLocked(const Handle handle, const bool locked) { + Event* evt = g_object_pool.Get<Event>(handle); + if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!evt->permanent_locked) { evt->locked = locked; } - return 0; + return RESULT_SUCCESS; } /** @@ -86,16 +81,16 @@ Result SetEventLocked(const Handle handle, const bool locked) { * @param handle Handle to event to signal * @return Result of operation, 0 on success, otherwise error code */ -Result SignalEvent(const Handle handle) { - Event* evt = g_object_pool.GetFast<Event>(handle); - _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); +ResultCode SignalEvent(const Handle handle) { + Event* evt = g_object_pool.Get<Event>(handle); + if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); // Resume threads waiting for event to signal bool event_caught = false; for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { ResumeThreadFromWait( evt->waiting_threads[i]); - // If any thread is signalled awake by this event, assume the event was "caught" and reset + // If any thread is signalled awake by this event, assume the event was "caught" and reset // the event. This will result in the next thread waiting on the event to block. Otherwise, // the event will not be reset, and the next thread to call WaitSynchronization on it will // not block. Not sure if this is correct behavior, but it seems to work. @@ -106,7 +101,7 @@ Result SignalEvent(const Handle handle) { if (!evt->permanent_locked) { evt->locked = event_caught; } - return 0; + return RESULT_SUCCESS; } /** @@ -114,14 +109,14 @@ Result SignalEvent(const Handle handle) { * @param handle Handle to event to clear * @return Result of operation, 0 on success, otherwise error code */ -Result ClearEvent(Handle handle) { - Event* evt = g_object_pool.GetFast<Event>(handle); - _assert_msg_(KERNEL, (evt != nullptr), "called, but event is nullptr!"); +ResultCode ClearEvent(Handle handle) { + Event* evt = g_object_pool.Get<Event>(handle); + if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!evt->permanent_locked) { evt->locked = true; } - return 0; + return RESULT_SUCCESS; } /** diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index c39b33180..73aec4e79 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #pragma once @@ -15,31 +15,27 @@ namespace Kernel { * Changes whether an event is locked or not * @param handle Handle to event to change * @param locked Boolean locked value to set event - * @return Result of operation, 0 on success, otherwise error code */ -Result SetEventLocked(const Handle handle, const bool locked); +ResultCode SetEventLocked(const Handle handle, const bool locked); /** * Hackish function to set an events permanent lock state, used to pass through synch blocks * @param handle Handle to event to change * @param permanent_locked Boolean permanent locked value to set event - * @return Result of operation, 0 on success, otherwise error code */ -Result SetPermanentLock(Handle handle, const bool permanent_locked); +ResultCode SetPermanentLock(Handle handle, const bool permanent_locked); /** * Signals an event * @param handle Handle to event to signal - * @return Result of operation, 0 on success, otherwise error code */ -Result SignalEvent(const Handle handle); +ResultCode SignalEvent(const Handle handle); /** * Clears an event * @param handle Handle to event to clear - * @return Result of operation, 0 on success, otherwise error code */ -Result ClearEvent(Handle handle); +ResultCode ClearEvent(Handle handle); /** * Creates an event diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 88cbc1af5..6a690e915 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -1,18 +1,20 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. + +#include <algorithm> #include "common/common.h" #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" -#include "core/hle/kernel/archive.h" namespace Kernel { Handle g_main_thread = 0; ObjectPool g_object_pool; +u64 g_program_id = 0; ObjectPool::ObjectPool() { next_id = INITIAL_NEXT_ID; @@ -33,11 +35,11 @@ Handle ObjectPool::Create(Object* obj, int range_bottom, int range_top) { return i + HANDLE_OFFSET; } } - ERROR_LOG(HLE, "Unable to allocate kernel object, too many objects slots in use."); + LOG_ERROR(Kernel, "Unable to allocate kernel object, too many objects slots in use."); return 0; } -bool ObjectPool::IsValid(Handle handle) { +bool ObjectPool::IsValid(Handle handle) const { int index = handle - HANDLE_OFFSET; if (index < 0) return false; @@ -60,7 +62,7 @@ void ObjectPool::Clear() { Object* &ObjectPool::operator [](Handle handle) { - _dbg_assert_msg_(KERNEL, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); + _dbg_assert_msg_(Kernel, IsValid(handle), "GRABBING UNALLOCED KERNEL OBJ"); return pool[handle - HANDLE_OFFSET]; } @@ -68,37 +70,30 @@ void ObjectPool::List() { for (int i = 0; i < MAX_COUNT; i++) { if (occupied[i]) { if (pool[i]) { - INFO_LOG(KERNEL, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), + LOG_DEBUG(Kernel, "KO %i: %s \"%s\"", i + HANDLE_OFFSET, pool[i]->GetTypeName().c_str(), pool[i]->GetName().c_str()); } } } } -int ObjectPool::GetCount() { - int count = 0; - for (int i = 0; i < MAX_COUNT; i++) { - if (occupied[i]) - count++; - } - return count; +int ObjectPool::GetCount() const { + return std::count(occupied.begin(), occupied.end(), true); } Object* ObjectPool::CreateByIDType(int type) { - ERROR_LOG(COMMON, "Unimplemented: %d.", type); + LOG_ERROR(Kernel, "Unimplemented: %d.", type); return nullptr; } /// Initialize the kernel void Init() { Kernel::ThreadingInit(); - Kernel::ArchiveInit(); } /// Shutdown the kernel void Shutdown() { Kernel::ThreadingShutdown(); - Kernel::ArchiveShutdown(); g_object_pool.Clear(); // Free all kernel objects } @@ -109,8 +104,6 @@ void Shutdown() { * @return True on success, otherwise false */ bool LoadExec(u32 entry_point) { - Init(); - Core::g_app_core->SetPC(entry_point); // 0x30 is the typical main thread priority I've seen used so far diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 867d1b89c..7123485be 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -1,12 +1,13 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #pragma once #include <array> #include <string> #include "common/common.h" +#include "core/hle/result.h" typedef u32 Handle; typedef s32 Result; @@ -21,7 +22,7 @@ enum KernelHandle { enum class HandleType : u32 { Unknown = 0, Port = 1, - Service = 2, + Session = 2, Event = 3, Mutex = 4, SharedMemory = 5, @@ -29,12 +30,9 @@ enum class HandleType : u32 { Thread = 7, Process = 8, AddressArbiter = 9, - File = 10, - Semaphore = 11, - Archive = 12, - Directory = 13, + Semaphore = 10, }; - + enum { DEFAULT_STACK_SIZE = 0x4000, }; @@ -52,21 +50,13 @@ public: virtual Kernel::HandleType GetHandleType() const = 0; /** - * Synchronize kernel object - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code + * Wait for kernel object to synchronize. + * @return True if the current thread should wait as a result of the wait */ - virtual Result SyncRequest(bool* wait) { - ERROR_LOG(KERNEL, "(UNIMPLEMENTED)"); - return -1; + virtual ResultVal<bool> WaitSynchronization() { + LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); + return UnimplementedFunction(ErrorModule::Kernel); } - - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - virtual Result WaitSynchronization(bool* wait) = 0; }; class ObjectPool : NonCopyable { @@ -80,38 +70,29 @@ public: static Object* CreateByIDType(int type); template <class T> - u32 Destroy(Handle handle) { - u32 error; - if (Get<T>(handle, error)) { + void Destroy(Handle handle) { + if (Get<T>(handle)) { occupied[handle - HANDLE_OFFSET] = false; delete pool[handle - HANDLE_OFFSET]; } - return error; - }; + } - bool IsValid(Handle handle); + bool IsValid(Handle handle) const; template <class T> - T* Get(Handle handle, u32& outError) { + T* Get(Handle handle) { if (handle < HANDLE_OFFSET || handle >= HANDLE_OFFSET + MAX_COUNT || !occupied[handle - HANDLE_OFFSET]) { - // Tekken 6 spams 0x80020001 gets wrong with no ill effects, also on the real PSP - if (handle != 0 && (u32)handle != 0x80020001) { - WARN_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); + if (handle != 0) { + LOG_ERROR(Kernel, "Bad object handle %08x", handle); } - outError = 0;//T::GetMissingErrorCode(); - return 0; + return nullptr; } else { - // Previously we had a dynamic_cast here, but since RTTI was disabled traditionally, - // it just acted as a static case and everything worked. This means that we will never - // see the Wrong type object error below, but we'll just have to live with that danger. - T* t = static_cast<T*>(pool[handle - HANDLE_OFFSET]); - if (t == 0 || t->GetHandleType() != T::GetStaticHandleType()) { - WARN_LOG(KERNEL, "Kernel: Wrong object type for %i (%08x)", handle, handle); - outError = 0;//T::GetMissingErrorCode(); - return 0; + Object* t = pool[handle - HANDLE_OFFSET]; + if (t->GetHandleType() != T::GetStaticHandleType()) { + LOG_ERROR(Kernel, "Wrong object type for %08x", handle); + return nullptr; } - outError = 0;//SCE_KERNEL_ERROR_OK; - return t; + return static_cast<T*>(t); } } @@ -119,7 +100,7 @@ public: template <class T> T *GetFast(Handle handle) { const Handle realHandle = handle - HANDLE_OFFSET; - _dbg_assert_(KERNEL, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); + _dbg_assert_(Kernel, realHandle >= 0 && realHandle < MAX_COUNT && occupied[realHandle]); return static_cast<T*>(pool[realHandle]); } @@ -139,9 +120,9 @@ public: } bool GetIDType(Handle handle, HandleType* type) const { - if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || - !occupied[handle - HANDLE_OFFSET]) { - ERROR_LOG(KERNEL, "Kernel: Bad object handle %i (%08x)", handle, handle); + if ((handle < HANDLE_OFFSET) || (handle >= HANDLE_OFFSET + MAX_COUNT) || + !occupied[handle - HANDLE_OFFSET]) { + LOG_ERROR(Kernel, "Bad object handle %08X", handle); return false; } Object* t = pool[handle - HANDLE_OFFSET]; @@ -152,10 +133,10 @@ public: Object* &operator [](Handle handle); void List(); void Clear(); - int GetCount(); + int GetCount() const; private: - + enum { MAX_COUNT = 0x1000, HANDLE_OFFSET = 0x100, @@ -170,6 +151,12 @@ private: extern ObjectPool g_object_pool; extern Handle g_main_thread; +/// The ID code of the currently running game +/// TODO(Subv): This variable should not be here, +/// we need a way to store information about the currently loaded application +/// for later query during runtime, maybe using the LDR service? +extern u64 g_program_id; + /// Initialize the kernel void Init(); diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index fcfd061ac..5a173e129 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #include <map> #include <vector> @@ -27,32 +27,7 @@ public: std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex std::string name; ///< Name of mutex (optional) - /** - * Synchronize kernel object - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result SyncRequest(bool* wait) override { - // TODO(bunnei): ImplementMe - locked = true; - return 0; - } - - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - // TODO(bunnei): ImplementMe - *wait = locked; - - if (locked) { - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); - } - - return 0; - } + ResultVal<bool> WaitSynchronization() override; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -60,21 +35,46 @@ public: typedef std::multimap<Handle, Handle> MutexMap; static MutexMap g_mutex_held_locks; -void MutexAcquireLock(Mutex* mutex, Handle thread) { +/** + * Acquires the specified mutex for the specified thread + * @param mutex Mutex that is to be acquired + * @param thread Thread that will acquired + */ +void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThreadHandle()) { g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); mutex->lock_thread = thread; } -void MutexAcquireLock(Mutex* mutex) { - Handle thread = GetCurrentThreadHandle(); +bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { MutexAcquireLock(mutex, thread); + Kernel::ResumeThreadFromWait(thread); + return true; +} + +/** + * Resumes a thread waiting for the specified mutex + * @param mutex The mutex that some thread is waiting on + */ +void ResumeWaitingThread(Mutex* mutex) { + // Find the next waiting thread for the mutex... + if (mutex->waiting_threads.empty()) { + // Reset mutex lock thread handle, nothing is waiting + mutex->locked = false; + mutex->lock_thread = -1; + } + else { + // Resume the next waiting thread and re-lock the mutex + std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); + ReleaseMutexForThread(mutex, *iter); + mutex->waiting_threads.erase(iter); + } } void MutexEraseLock(Mutex* mutex) { Handle handle = mutex->GetHandle(); auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - if ((*iter).second == handle) { + if (iter->second == handle) { g_mutex_held_locks.erase(iter); break; } @@ -82,6 +82,19 @@ void MutexEraseLock(Mutex* mutex) { mutex->lock_thread = -1; } +void ReleaseThreadMutexes(Handle thread) { + auto locked = g_mutex_held_locks.equal_range(thread); + + // Release every mutex that the thread holds, and resume execution on the waiting threads + for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { + Mutex* mutex = g_object_pool.GetFast<Mutex>(iter->second); + ResumeWaitingThread(mutex); + } + + // Erase all the locks that this thread holds + g_mutex_held_locks.erase(thread); +} + bool LockMutex(Mutex* mutex) { // Mutex alread locked? if (mutex->locked) { @@ -91,43 +104,27 @@ bool LockMutex(Mutex* mutex) { return true; } -bool ReleaseMutexForThread(Mutex* mutex, Handle thread) { - MutexAcquireLock(mutex, thread); - Kernel::ResumeThreadFromWait(thread); - return true; -} - bool ReleaseMutex(Mutex* mutex) { MutexEraseLock(mutex); - bool woke_threads = false; - - // Find the next waiting thread for the mutex... - while (!woke_threads && !mutex->waiting_threads.empty()) { - std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); - woke_threads |= ReleaseMutexForThread(mutex, *iter); - mutex->waiting_threads.erase(iter); - } - // Reset mutex lock thread handle, nothing is waiting - if (!woke_threads) { - mutex->locked = false; - mutex->lock_thread = -1; - } - return woke_threads; + ResumeWaitingThread(mutex); + return true; } /** * Releases a mutex * @param handle Handle to mutex to release */ -Result ReleaseMutex(Handle handle) { - Mutex* mutex = Kernel::g_object_pool.GetFast<Mutex>(handle); - - _assert_msg_(KERNEL, (mutex != nullptr), "ReleaseMutex tried to release a nullptr mutex!"); +ResultCode ReleaseMutex(Handle handle) { + Mutex* mutex = Kernel::g_object_pool.Get<Mutex>(handle); + if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); if (!ReleaseMutex(mutex)) { - return -1; + // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure + // what error condition this is supposed to be signaling. + return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel, + ErrorSummary::NothingHappened, ErrorLevel::Temporary); } - return 0; + return RESULT_SUCCESS; } /** @@ -167,4 +164,17 @@ Handle CreateMutex(bool initial_locked, const std::string& name) { return handle; } +ResultVal<bool> Mutex::WaitSynchronization() { + bool wait = locked; + if (locked) { + Kernel::WaitCurrentThread(WAITTYPE_MUTEX, GetHandle()); + } + else { + // Lock the mutex when the first thread accesses it + locked = true; + MutexAcquireLock(this); + } + + return MakeResult<bool>(wait); +} } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index 7d7b5137e..7f4909a6e 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #pragma once @@ -13,9 +13,8 @@ namespace Kernel { /** * Releases a mutex * @param handle Handle to mutex to release - * @return Result of operation, 0 on success, otherwise error code */ -Result ReleaseMutex(Handle handle); +ResultCode ReleaseMutex(Handle handle); /** * Creates a mutex @@ -25,4 +24,10 @@ Result ReleaseMutex(Handle handle); */ Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); +/** + * Releases all the mutexes held by the specified thread + * @param thread Thread that is holding the mutexes + */ +void ReleaseThreadMutexes(Handle thread); + } // namespace diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp new file mode 100644 index 000000000..6f56da8a9 --- /dev/null +++ b/src/core/hle/kernel/semaphore.cpp @@ -0,0 +1,94 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include <queue> + +#include "common/common.h" + +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/semaphore.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +class Semaphore : public Object { +public: + std::string GetTypeName() const override { return "Semaphore"; } + std::string GetName() const override { return name; } + + static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Semaphore; } + Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Semaphore; } + + u32 max_count; ///< Maximum number of simultaneous holders the semaphore can have + u32 available_count; ///< Number of free slots left in the semaphore + std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore + std::string name; ///< Name of semaphore (optional) + + /** + * Tests whether a semaphore still has free slots + * @return Whether the semaphore is available + */ + bool IsAvailable() const { + return available_count > 0; + } + + ResultVal<bool> WaitSynchronization() override { + bool wait = !IsAvailable(); + + if (wait) { + Kernel::WaitCurrentThread(WAITTYPE_SEMA, GetHandle()); + waiting_threads.push(GetCurrentThreadHandle()); + } else { + --available_count; + } + + return MakeResult<bool>(wait); + } +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +ResultCode CreateSemaphore(Handle* handle, u32 initial_count, + u32 max_count, const std::string& name) { + + if (initial_count > max_count) + return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, + ErrorSummary::WrongArgument, ErrorLevel::Permanent); + + Semaphore* semaphore = new Semaphore; + *handle = g_object_pool.Create(semaphore); + + // When the semaphore is created, some slots are reserved for other threads, + // and the rest is reserved for the caller thread + semaphore->max_count = max_count; + semaphore->available_count = initial_count; + semaphore->name = name; + + return RESULT_SUCCESS; +} + +ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { + Semaphore* semaphore = g_object_pool.Get<Semaphore>(handle); + if (semaphore == nullptr) + return InvalidHandle(ErrorModule::Kernel); + + if (semaphore->max_count - semaphore->available_count < release_count) + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + + *count = semaphore->available_count; + semaphore->available_count += release_count; + + // Notify some of the threads that the semaphore has been released + // stop once the semaphore is full again or there are no more waiting threads + while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { + Kernel::ResumeThreadFromWait(semaphore->waiting_threads.front()); + semaphore->waiting_threads.pop(); + --semaphore->available_count; + } + + return RESULT_SUCCESS; +} + +} // namespace diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h new file mode 100644 index 000000000..f0075fdb8 --- /dev/null +++ b/src/core/hle/kernel/semaphore.h @@ -0,0 +1,32 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "common/common_types.h" + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +/** + * Creates a semaphore. + * @param handle Pointer to the handle of the newly created object + * @param initial_count Number of slots reserved for other threads + * @param max_count Maximum number of slots the semaphore can have + * @param name Optional name of semaphore + * @return ResultCode of the error + */ +ResultCode CreateSemaphore(Handle* handle, u32 initial_count, u32 max_count, const std::string& name = "Unknown"); + +/** + * Releases a certain number of slots from a semaphore. + * @param count The number of free slots the semaphore had before this call + * @param handle The handle of the semaphore to release + * @param release_count The number of slots to release + * @return ResultCode of the error + */ +ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); + +} // namespace diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h new file mode 100644 index 000000000..06ae4bc39 --- /dev/null +++ b/src/core/hle/kernel/session.h @@ -0,0 +1,58 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header + +/** + * Returns a pointer to the command buffer in kernel memory + * @param offset Optional offset into command buffer + * @return Pointer to command buffer + */ +inline static u32* GetCommandBuffer(const int offset=0) { + return (u32*)Memory::GetPointer(Memory::KERNEL_MEMORY_VADDR + kCommandHeaderOffset + offset); +} + +/** + * Kernel object representing the client endpoint of an IPC session. Sessions are the basic CTR-OS + * primitive for communication between different processes, and are used to implement service calls + * to the various system services. + * + * To make a service call, the client must write the command header and parameters to the buffer + * located at offset 0x80 of the TLS (Thread-Local Storage) area, then execute a SendSyncRequest + * SVC call with its Session handle. The kernel will read the command header, using it to marshall + * the parameters to the process at the server endpoint of the session. After the server replies to + * the request, the response is marshalled back to the caller's TLS buffer and control is + * transferred back to it. + * + * In Citra, only the client endpoint is currently implemented and only HLE calls, where the IPC + * request is answered by C++ code in the emulator, are supported. When SendSyncRequest is called + * with the session handle, this class's SyncRequest method is called, which should read the TLS + * buffer and emulate the call accordingly. Since the code can directly read the emulated memory, + * no parameter marshalling is done. + * + * In the long term, this should be turned into the full-fledged IPC mechanism implemented by + * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as + * opposed to HLE simulations. + */ +class Session : public Object { +public: + std::string GetTypeName() const override { return "Session"; } + + static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::Session; } + Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::Session; } + + /** + * Handles a synchronous call to this session using HLE emulation. Emulated <-> emulated calls + * aren't supported yet. + */ + virtual ResultVal<bool> SyncRequest() = 0; +}; + +} diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 6bd5e2728..3c8c502c6 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #include "common/common.h" @@ -16,17 +16,6 @@ public: static Kernel::HandleType GetStaticHandleType() { return Kernel::HandleType::SharedMemory; } Kernel::HandleType GetHandleType() const override { return Kernel::HandleType::SharedMemory; } - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - // TODO(bunnei): ImplementMe - ERROR_LOG(OSHLE, "(UNIMPLEMENTED)"); - return 0; - } - u32 base_address; ///< Address of shared memory block in RAM MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) @@ -48,11 +37,6 @@ SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { return shared_memory; } -/** - * Creates a shared memory object - * @param name Optional name of shared memory object - * @return Handle of newly created shared memory object - */ Handle CreateSharedMemory(const std::string& name) { Handle handle; CreateSharedMemory(handle, name); @@ -67,39 +51,36 @@ Handle CreateSharedMemory(const std::string& name) { * @param other_permissions Memory block map other permissions (specified by SVC field) * @return Result of operation, 0 on success, otherwise error code */ -Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, +ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, MemoryPermission other_permissions) { if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { - ERROR_LOG(KERNEL, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", - handle); - return -1; + LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", + handle, address); + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle); - _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle); + SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); + if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); shared_memory->base_address = address; shared_memory->permissions = permissions; shared_memory->other_permissions = other_permissions; - return 0; + return RESULT_SUCCESS; } -/** - * Gets a pointer to the shared memory block - * @param handle Shared memory block handle - * @param offset Offset from the start of the shared memory block to get pointer - * @return Pointer to the shared memory block from the specified offset - */ -u8* GetSharedMemoryPointer(Handle handle, u32 offset) { - SharedMemory* shared_memory = Kernel::g_object_pool.GetFast<SharedMemory>(handle); - _assert_msg_(KERNEL, (shared_memory != nullptr), "handle 0x%08X is not valid!", handle); +ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { + SharedMemory* shared_memory = Kernel::g_object_pool.Get<SharedMemory>(handle); + if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); if (0 != shared_memory->base_address) - return Memory::GetPointer(shared_memory->base_address + offset); + return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); - ERROR_LOG(KERNEL, "memory block handle=0x%08X not mapped!", handle); - return nullptr; + LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); + // TODO(yuriks): Verify error code. + return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, + ErrorSummary::InvalidState, ErrorLevel::Permanent); } } // namespace diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 6204d8a45..bb778ec26 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #pragma once @@ -36,9 +36,8 @@ Handle CreateSharedMemory(const std::string& name="Unknown"); * @param address Address in system memory to map shared memory block to * @param permissions Memory block map permissions (specified by SVC field) * @param other_permissions Memory block map other permissions (specified by SVC field) - * @return Result of operation, 0 on success, otherwise error code */ -Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, +ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions, MemoryPermission other_permissions); /** @@ -47,6 +46,6 @@ Result MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, * @param offset Offset from the start of the shared memory block to get pointer * @return Pointer to the shared memory block from the specified offset */ -u8* GetSharedMemoryPointer(Handle handle, u32 offset); +ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset); } // namespace diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index e15590c49..1c04701de 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -1,6 +1,6 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #include <algorithm> #include <list> @@ -11,10 +11,12 @@ #include "common/thread_queue_list.h" #include "core/core.h" -#include "core/mem_map.h" #include "core/hle/hle.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" +#include "core/hle/kernel/mutex.h" +#include "core/hle/result.h" +#include "core/mem_map.h" namespace Kernel { @@ -33,25 +35,23 @@ public: inline bool IsWaiting() const { return (status & THREADSTATUS_WAIT) != 0; } inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } - /** - * Wait for kernel object to synchronize - * @param wait Boolean wait set if current thread should wait as a result of sync operation - * @return Result of operation, 0 on success, otherwise error code - */ - Result WaitSynchronization(bool* wait) override { - if (status != THREADSTATUS_DORMANT) { + ResultVal<bool> WaitSynchronization() override { + const bool wait = status != THREADSTATUS_DORMANT; + if (wait) { Handle thread = GetCurrentThreadHandle(); if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { waiting_threads.push_back(thread); } WaitCurrentThread(WAITTYPE_THREADEND, this->GetHandle()); - *wait = true; } - return 0; + + return MakeResult<bool>(wait); } ThreadContext context; + u32 thread_id; + u32 status; u32 entry_point; u32 stack_top; @@ -64,6 +64,7 @@ public: WaitType wait_type; Handle wait_handle; + VAddr wait_address; std::vector<Handle> waiting_threads; @@ -71,17 +72,20 @@ public: }; // Lists all thread ids that aren't deleted/etc. -std::vector<Handle> g_thread_queue; +static std::vector<Handle> thread_queue; // Lists only ready thread ids. -Common::ThreadQueueList<Handle> g_thread_ready_queue; +static Common::ThreadQueueList<Handle> thread_ready_queue; + +static Handle current_thread_handle; +static Thread* current_thread; -Handle g_current_thread_handle; -Thread* g_current_thread; +static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup +static u32 next_thread_id; ///< The next available thread id /// Gets the current thread inline Thread* GetCurrentThread() { - return g_current_thread; + return current_thread; } /// Gets the current thread handle @@ -91,8 +95,8 @@ Handle GetCurrentThreadHandle() { /// Sets the current thread inline void SetCurrentThread(Thread* t) { - g_current_thread = t; - g_current_thread_handle = t->GetHandle(); + current_thread = t; + current_thread_handle = t->GetHandle(); } /// Saves the current CPU context @@ -113,7 +117,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { t->context.pc = t->context.reg_15 = t->entry_point; t->context.sp = t->stack_top; t->context.cpsr = 0x1F; // Usermode - + // TODO(bunnei): This instructs the CPU core to start the execution as if it is "resuming" a // thread. This is somewhat Sky-Eye specific, and should be re-architected in the future to be // agnostic of the CPU core. @@ -124,6 +128,7 @@ void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { } t->wait_type = WAITTYPE_NONE; t->wait_handle = 0; + t->wait_address = 0; } /// Change a thread to "ready" state @@ -131,40 +136,44 @@ void ChangeReadyState(Thread* t, bool ready) { Handle handle = t->GetHandle(); if (t->IsReady()) { if (!ready) { - g_thread_ready_queue.remove(t->current_priority, handle); + thread_ready_queue.remove(t->current_priority, handle); } } else if (ready) { if (t->IsRunning()) { - g_thread_ready_queue.push_front(t->current_priority, handle); + thread_ready_queue.push_front(t->current_priority, handle); } else { - g_thread_ready_queue.push_back(t->current_priority, handle); + thread_ready_queue.push_back(t->current_priority, handle); } t->status = THREADSTATUS_READY; } } /// Verify that a thread has not been released from waiting -inline bool VerifyWait(const Handle& handle, WaitType type, Handle wait_handle) { - Thread* thread = g_object_pool.GetFast<Thread>(handle); - _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); - - if (type != thread->wait_type || wait_handle != thread->wait_handle) - return false; +static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle) { + _dbg_assert_(Kernel, thread != nullptr); + return (type == thread->wait_type) && (wait_handle == thread->wait_handle) && (thread->IsWaiting()); +} - return true; +/// Verify that a thread has not been released from waiting (with wait address) +static bool VerifyWait(const Thread* thread, WaitType type, Handle wait_handle, VAddr wait_address) { + _dbg_assert_(Kernel, thread != nullptr); + return VerifyWait(thread, type, wait_handle) && (wait_address == thread->wait_address); } /// Stops the current thread -void StopThread(Handle handle, const char* reason) { - Thread* thread = g_object_pool.GetFast<Thread>(handle); - _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); - +ResultCode StopThread(Handle handle, const char* reason) { + Thread* thread = g_object_pool.Get<Thread>(handle); + if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); + + // Release all the mutexes that this thread holds + ReleaseThreadMutexes(handle); + ChangeReadyState(thread, false); thread->status = THREADSTATUS_DORMANT; - for (size_t i = 0; i < thread->waiting_threads.size(); ++i) { - const Handle waiting_thread = thread->waiting_threads[i]; + for (Handle waiting_handle : thread->waiting_threads) { + Thread* waiting_thread = g_object_pool.Get<Thread>(waiting_handle); if (VerifyWait(waiting_thread, WAITTYPE_THREADEND, handle)) { - ResumeThreadFromWait(waiting_thread); + ResumeThreadFromWait(waiting_handle); } } thread->waiting_threads.clear(); @@ -172,6 +181,9 @@ void StopThread(Handle handle, const char* reason) { // Stopped threads are never waiting. thread->wait_type = WAITTYPE_NONE; thread->wait_handle = 0; + thread->wait_address = 0; + + return RESULT_SUCCESS; } /// Changes a threads state @@ -181,10 +193,10 @@ void ChangeThreadState(Thread* t, ThreadStatus new_status) { } ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; - + if (new_status == THREADSTATUS_WAIT) { if (t->wait_type == WAITTYPE_NONE) { - ERROR_LOG(KERNEL, "Waittype none not allowed"); + LOG_ERROR(Kernel, "Waittype none not allowed"); } } } @@ -195,13 +207,15 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (const auto& handle : g_thread_queue) { + for (Handle handle : thread_queue) { + Thread* thread = g_object_pool.Get<Thread>(handle); - // TODO(bunnei): Verify arbiter address... - if (!VerifyWait(handle, WAITTYPE_ARB, arbiter)) + if (!VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) continue; - Thread* thread = g_object_pool.GetFast<Thread>(handle); + if (thread == nullptr) + continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + if(thread->current_priority <= priority) { highest_priority_thread = handle; priority = thread->current_priority; @@ -216,12 +230,12 @@ Handle ArbitrateHighestPriorityThread(u32 arbiter, u32 address) { /// Arbitrate all threads currently waiting void ArbitrateAllThreads(u32 arbiter, u32 address) { - + // Iterate through threads, find highest priority thread that is waiting to be arbitrated... - for (const auto& handle : g_thread_queue) { + for (Handle handle : thread_queue) { + Thread* thread = g_object_pool.Get<Thread>(handle); - // TODO(bunnei): Verify arbiter address... - if (VerifyWait(handle, WAITTYPE_ARB, arbiter)) + if (VerifyWait(thread, WAITTYPE_ARB, arbiter, address)) ResumeThreadFromWait(handle); } } @@ -238,11 +252,11 @@ void CallThread(Thread* t) { /// Switches CPU context to that of the specified thread void SwitchContext(Thread* t) { Thread* cur = GetCurrentThread(); - + // Save context for current thread if (cur) { SaveContext(cur->context); - + if (cur->IsRunning()) { ChangeReadyState(cur, true); } @@ -263,23 +277,18 @@ void SwitchContext(Thread* t) { Thread* NextThread() { Handle next; Thread* cur = GetCurrentThread(); - + if (cur && cur->IsRunning()) { - next = g_thread_ready_queue.pop_first_better(cur->current_priority); + next = thread_ready_queue.pop_first_better(cur->current_priority); } else { - next = g_thread_ready_queue.pop_first(); + next = thread_ready_queue.pop_first(); } if (next == 0) { return nullptr; } - return Kernel::g_object_pool.GetFast<Thread>(next); + return Kernel::g_object_pool.Get<Thread>(next); } -/** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread - */ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { Thread* thread = GetCurrentThread(); thread->wait_type = wait_type; @@ -287,10 +296,14 @@ void WaitCurrentThread(WaitType wait_type, Handle wait_handle) { ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } +void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address) { + WaitCurrentThread(wait_type, wait_handle); + GetCurrentThread()->wait_address = wait_address; +} + /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle) { - u32 error; - Thread* thread = Kernel::g_object_pool.Get<Thread>(handle, error); + Thread* thread = Kernel::g_object_pool.Get<Thread>(handle); if (thread) { thread->status &= ~THREADSTATUS_WAIT; if (!(thread->status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { @@ -305,12 +318,12 @@ void DebugThreadQueue() { if (!thread) { return; } - INFO_LOG(KERNEL, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); - for (u32 i = 0; i < g_thread_queue.size(); i++) { - Handle handle = g_thread_queue[i]; - s32 priority = g_thread_ready_queue.contains(handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThreadHandle()); + for (u32 i = 0; i < thread_queue.size(); i++) { + Handle handle = thread_queue[i]; + s32 priority = thread_ready_queue.contains(handle); if (priority != -1) { - INFO_LOG(KERNEL, "0x%02X 0x%08X", priority, handle); + LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, handle); } } } @@ -319,16 +332,17 @@ void DebugThreadQueue() { Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 priority, s32 processor_id, u32 stack_top, int stack_size) { - _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), - "CreateThread priority=%d, outside of allowable range!", priority) + _assert_msg_(KERNEL, (priority >= THREADPRIO_HIGHEST && priority <= THREADPRIO_LOWEST), + "priority=%d, outside of allowable range!", priority) Thread* thread = new Thread; handle = Kernel::g_object_pool.Create(thread); - g_thread_queue.push_back(handle); - g_thread_ready_queue.prepare(priority); + thread_queue.push_back(handle); + thread_ready_queue.prepare(priority); + thread->thread_id = next_thread_id++; thread->status = THREADSTATUS_DORMANT; thread->entry_point = entry_point; thread->stack_top = stack_top; @@ -337,6 +351,7 @@ Thread* CreateThread(Handle& handle, const char* name, u32 entry_point, s32 prio thread->processor_id = processor_id; thread->wait_type = WAITTYPE_NONE; thread->wait_handle = 0; + thread->wait_address = 0; thread->name = name; return thread; @@ -347,28 +362,28 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 u32 stack_top, int stack_size) { if (name == nullptr) { - ERROR_LOG(KERNEL, "CreateThread(): nullptr name"); + LOG_ERROR(Kernel_SVC, "nullptr name"); return -1; } if ((u32)stack_size < 0x200) { - ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid stack_size=0x%08X", name, + LOG_ERROR(Kernel_SVC, "(name=%s): invalid stack_size=0x%08X", name, stack_size); return -1; } if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); - WARN_LOG(KERNEL, "CreateThread(name=%s): invalid priority=0x%08X, clamping to %08X", + LOG_WARNING(Kernel_SVC, "(name=%s): invalid priority=%d, clamping to %d", name, priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm // validity of this priority = new_priority; } if (!Memory::GetPointer(entry_point)) { - ERROR_LOG(KERNEL, "CreateThread(name=%s): invalid entry %08x", name, entry_point); + LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name, entry_point); return -1; } Handle handle; - Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, + Thread* thread = CreateThread(handle, name, entry_point, priority, processor_id, stack_top, stack_size); ResetThread(thread, arg, 0); @@ -378,26 +393,30 @@ Handle CreateThread(const char* name, u32 entry_point, s32 priority, u32 arg, s3 } /// Get the priority of the thread specified by handle -u32 GetThreadPriority(const Handle handle) { - Thread* thread = g_object_pool.GetFast<Thread>(handle); - _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); - return thread->current_priority; +ResultVal<u32> GetThreadPriority(const Handle handle) { + Thread* thread = g_object_pool.Get<Thread>(handle); + if (thread == nullptr) return InvalidHandle(ErrorModule::Kernel); + + return MakeResult<u32>(thread->current_priority); } /// Set the priority of the thread specified by handle -Result SetThreadPriority(Handle handle, s32 priority) { +ResultCode SetThreadPriority(Handle handle, s32 priority) { Thread* thread = nullptr; if (!handle) { thread = GetCurrentThread(); // TODO(bunnei): Is this correct behavior? } else { - thread = g_object_pool.GetFast<Thread>(handle); + thread = g_object_pool.Get<Thread>(handle); + if (thread == nullptr) { + return InvalidHandle(ErrorModule::Kernel); + } } _assert_msg_(KERNEL, (thread != nullptr), "called, but thread is nullptr!"); // If priority is invalid, clamp to valid range if (priority < THREADPRIO_HIGHEST || priority > THREADPRIO_LOWEST) { s32 new_priority = CLAMP(priority, THREADPRIO_HIGHEST, THREADPRIO_LOWEST); - WARN_LOG(KERNEL, "invalid priority=0x%08X, clamping to %08X", priority, new_priority); + LOG_WARNING(Kernel_SVC, "invalid priority=%d, clamping to %d", priority, new_priority); // TODO(bunnei): Clamping to a valid priority is not necessarily correct behavior... Confirm // validity of this priority = new_priority; @@ -405,37 +424,37 @@ Result SetThreadPriority(Handle handle, s32 priority) { // Change thread priority s32 old = thread->current_priority; - g_thread_ready_queue.remove(old, handle); + thread_ready_queue.remove(old, handle); thread->current_priority = priority; - g_thread_ready_queue.prepare(thread->current_priority); + thread_ready_queue.prepare(thread->current_priority); // Change thread status to "ready" and push to ready queue if (thread->IsRunning()) { thread->status = (thread->status & ~THREADSTATUS_RUNNING) | THREADSTATUS_READY; } if (thread->IsReady()) { - g_thread_ready_queue.push_back(thread->current_priority, handle); + thread_ready_queue.push_back(thread->current_priority, handle); } - return 0; + return RESULT_SUCCESS; } /// Sets up the primary application thread Handle SetupMainThread(s32 priority, int stack_size) { Handle handle; - + // Initialize new "main" thread - Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, + Thread* thread = CreateThread(handle, "main", Core::g_app_core->GetPC(), priority, THREADPROCESSORID_0, Memory::SCRATCHPAD_VADDR_END, stack_size); - + ResetThread(thread, 0, 0); - + // If running another thread already, set it to "ready" state Thread* cur = GetCurrentThread(); if (cur && cur->IsRunning()) { ChangeReadyState(cur, true); } - + // Run new "main" thread SetCurrentThread(thread); thread->status = THREADSTATUS_RUNNING; @@ -451,13 +470,13 @@ void Reschedule() { Thread* next = NextThread(); HLE::g_reschedule = false; if (next > 0) { - INFO_LOG(KERNEL, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); - + LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); + SwitchContext(next); // Hack - There is no mechanism yet to waken the primary thread if it has been put to sleep // by a simulated VBLANK thread switch. So, we'll just immediately set it to "ready" again. - // This results in the current thread yielding on a VBLANK once, and then it will be + // This results in the current thread yielding on a VBLANK once, and then it will be // immediately placed back in the queue for execution. if (prev->wait_type == WAITTYPE_VBLANK) { ResumeThreadFromWait(prev->GetHandle()); @@ -465,9 +484,21 @@ void Reschedule() { } } +ResultCode GetThreadId(u32* thread_id, Handle handle) { + Thread* thread = g_object_pool.Get<Thread>(handle); + if (thread == nullptr) + return ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent); + + *thread_id = thread->thread_id; + + return RESULT_SUCCESS; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { + next_thread_id = INITIAL_THREAD_ID; } void ThreadingShutdown() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 39fa38b75..be7adface 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -1,11 +1,15 @@ // Copyright 2014 Citra Emulator Project / PPSSPP Project // Licensed under GPLv2 -// Refer to the license.txt file included. +// Refer to the license.txt file included. #pragma once #include "common/common_types.h" + +#include "core/mem_map.h" + #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" enum ThreadPriority { THREADPRIO_HIGHEST = 0, ///< Highest thread priority @@ -55,7 +59,15 @@ Handle SetupMainThread(s32 priority, int stack_size=Kernel::DEFAULT_STACK_SIZE); void Reschedule(); /// Stops the current thread -void StopThread(Handle thread, const char* reason); +ResultCode StopThread(Handle thread, const char* reason); + +/** + * Retrieves the ID of the specified thread handle + * @param thread_id Will contain the output thread id + * @param handle Handle to the thread we want + * @return Whether the function was successful or not + */ +ResultCode GetThreadId(u32* thread_id, Handle handle); /// Resumes a thread from waiting by marking it as "ready" void ResumeThreadFromWait(Handle handle); @@ -76,14 +88,22 @@ Handle GetCurrentThreadHandle(); */ void WaitCurrentThread(WaitType wait_type, Handle wait_handle=GetCurrentThreadHandle()); +/** + * Puts the current thread in the wait state for the given type + * @param wait_type Type of wait + * @param wait_handle Handle of Kernel object that we are waiting on, defaults to current thread + * @param wait_address Arbitration address used to resume from wait + */ +void WaitCurrentThread(WaitType wait_type, Handle wait_handle, VAddr wait_address); + /// Put current thread in a wait state - on WaitSynchronization void WaitThread_Synchronization(); /// Get the priority of the thread specified by handle -u32 GetThreadPriority(const Handle handle); +ResultVal<u32> GetThreadPriority(const Handle handle); /// Set the priority of the thread specified by handle -Result SetThreadPriority(Handle handle, s32 priority); +ResultCode SetThreadPriority(Handle handle, s32 priority); /// Initialize threading void ThreadingInit(); |