diff options
Diffstat (limited to 'src/core')
73 files changed, 1213 insertions, 836 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7aa81e885..6e602b0c5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -45,6 +45,8 @@ set(SRCS hle/kernel/client_port.cpp hle/kernel/client_session.cpp hle/kernel/event.cpp + hle/kernel/handle_table.cpp + hle/kernel/hle_ipc.cpp hle/kernel/kernel.cpp hle/kernel/memory.cpp hle/kernel/mutex.cpp @@ -57,6 +59,7 @@ set(SRCS hle/kernel/thread.cpp hle/kernel/timer.cpp hle/kernel/vm_manager.cpp + hle/kernel/wait_object.cpp hle/service/ac/ac.cpp hle/service/ac/ac_i.cpp hle/service/ac/ac_u.cpp @@ -153,8 +156,9 @@ set(SRCS hle/service/qtm/qtm_sp.cpp hle/service/qtm/qtm_u.cpp hle/service/service.cpp + hle/service/sm/sm.cpp + hle/service/sm/srv.cpp hle/service/soc_u.cpp - hle/service/srv.cpp hle/service/ssl_c.cpp hle/service/y2r_u.cpp hle/shared_page.cpp @@ -236,6 +240,8 @@ set(HEADERS hle/kernel/client_session.h hle/kernel/errors.h hle/kernel/event.h + hle/kernel/handle_table.h + hle/kernel/hle_ipc.h hle/kernel/kernel.h hle/kernel/memory.h hle/kernel/mutex.h @@ -249,6 +255,7 @@ set(HEADERS hle/kernel/thread.h hle/kernel/timer.h hle/kernel/vm_manager.h + hle/kernel/wait_object.h hle/result.h hle/service/ac/ac.h hle/service/ac/ac_i.h @@ -346,8 +353,9 @@ set(HEADERS hle/service/qtm/qtm_sp.h hle/service/qtm/qtm_u.h hle/service/service.h + hle/service/sm/sm.h + hle/service/sm/srv.h hle/service/soc_u.h - hle/service/srv.h hle/service/ssl_c.h hle/service/y2r_u.h hle/shared_page.h @@ -374,11 +382,7 @@ set(HEADERS telemetry_session.h ) -include_directories(../../externals/dynarmic/include) -include_directories(../../externals/cryptopp) - create_directory_groups(${SRCS} ${HEADERS}) - add_library(core STATIC ${SRCS} ${HEADERS}) target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) -target_link_libraries(core PRIVATE cryptopp dynarmic) +target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic) diff --git a/src/core/core.cpp b/src/core/core.cpp index 450e7566d..5429bcb26 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -3,7 +3,7 @@ // Refer to the license.txt file included. #include <memory> - +#include <utility> #include "audio_core/audio_core.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" @@ -26,6 +26,7 @@ namespace Core { /*static*/ System System::s_instance; System::ResultStatus System::RunLoop(int tight_loop) { + status = ResultStatus::Success; if (!cpu_core) { return ResultStatus::ErrorNotInitialized; } @@ -59,7 +60,7 @@ System::ResultStatus System::RunLoop(int tight_loop) { HW::Update(); Reschedule(); - return ResultStatus::Success; + return status; } System::ResultStatus System::SingleStep() { @@ -73,14 +74,25 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file LOG_CRITICAL(Core, "Failed to obtain loader for %s!", filepath.c_str()); return ResultStatus::ErrorGetLoader; } + std::pair<boost::optional<u32>, Loader::ResultStatus> system_mode = + app_loader->LoadKernelSystemMode(); + + if (system_mode.second != Loader::ResultStatus::Success) { + LOG_CRITICAL(Core, "Failed to determine system mode (Error %i)!", + static_cast<int>(system_mode.second)); + System::Shutdown(); - boost::optional<u32> system_mode{app_loader->LoadKernelSystemMode()}; - if (!system_mode) { - LOG_CRITICAL(Core, "Failed to determine system mode!"); - return ResultStatus::ErrorSystemMode; + switch (system_mode.second) { + case Loader::ResultStatus::ErrorEncrypted: + return ResultStatus::ErrorLoader_ErrorEncrypted; + case Loader::ResultStatus::ErrorInvalidFormat: + return ResultStatus::ErrorLoader_ErrorInvalidFormat; + default: + return ResultStatus::ErrorSystemMode; + } } - ResultStatus init_result{Init(emu_window, system_mode.get())}; + ResultStatus init_result{Init(emu_window, system_mode.first.get())}; if (init_result != ResultStatus::Success) { LOG_CRITICAL(Core, "Failed to initialize system (Error %i)!", init_result); System::Shutdown(); @@ -101,7 +113,8 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file return ResultStatus::ErrorLoader; } } - return ResultStatus::Success; + status = ResultStatus::Success; + return status; } void System::PrepareReschedule() { diff --git a/src/core/core.h b/src/core/core.h index 6af772831..4e3b6b409 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -40,7 +40,10 @@ public: ErrorLoader_ErrorEncrypted, ///< Error loading the specified application due to encryption ErrorLoader_ErrorInvalidFormat, ///< Error loading the specified application due to an /// invalid format + ErrorSystemFiles, ///< Error in finding system files + ErrorSharedFont, ///< Error in finding shared font ErrorVideoCore, ///< Error in the video core + ErrorUnknown ///< Any other error }; /** @@ -105,6 +108,17 @@ public: PerfStats perf_stats; FrameLimiter frame_limiter; + void SetStatus(ResultStatus new_status, const char* details = nullptr) { + status = new_status; + if (details) { + status_details = details; + } + } + + const std::string& GetStatusDetails() const { + return status_details; + } + private: /** * Initialize the emulated system. @@ -130,6 +144,9 @@ private: std::unique_ptr<Core::TelemetrySession> telemetry_session; static System s_instance; + + ResultStatus status = ResultStatus::Success; + std::string status_details = ""; }; inline ARM_Interface& CPU() { diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index 89455e39c..6d9007731 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -9,7 +9,9 @@ #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" +#include "core/core.h" #include "core/file_sys/archive_ncch.h" +#include "core/file_sys/errors.h" #include "core/file_sys/ivfc_archive.h" #include "core/hle/service/fs/archive.h" @@ -33,11 +35,44 @@ ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_NCCH::Open(const Path& path) { auto vec = path.AsBinary(); const u32* data = reinterpret_cast<u32*>(vec.data()); - std::string file_path = GetNCCHPath(mount_point, data[1], data[0]); + u32 high = data[1]; + u32 low = data[0]; + std::string file_path = GetNCCHPath(mount_point, high, low); auto file = std::make_shared<FileUtil::IOFile>(file_path, "rb"); if (!file->IsOpen()) { - return ResultCode(-1); // TODO(Subv): Find the right error code + // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list). + constexpr u32 shared_data_archive = 0x0004009B; + constexpr u32 system_data_archive = 0x000400DB; + + // Low Title IDs. + constexpr u32 mii_data = 0x00010202; + constexpr u32 region_manifest = 0x00010402; + constexpr u32 ng_word_list = 0x00010302; + + LOG_DEBUG(Service_FS, "Full Path: %s. Category: 0x%X. Path: 0x%X.", path.DebugStr().c_str(), + high, low); + + if (high == shared_data_archive) { + if (low == mii_data) { + LOG_ERROR(Service_FS, "Failed to get a handle for shared data archive: Mii data. "); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles, + "Mii data"); + } else if (low == region_manifest) { + LOG_ERROR(Service_FS, + "Failed to get a handle for shared data archive: region manifest."); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles, + "Region manifest"); + } + } else if (high == system_data_archive) { + if (low == ng_word_list) { + LOG_ERROR(Service_FS, + "Failed to get a handle for system data archive: NG bad word list."); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSystemFiles, + "NG bad word list"); + } + } + return ERROR_NOT_FOUND; } auto size = file->GetSize(); diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index 06c4c5a85..d7348c09d 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -3,7 +3,9 @@ // Refer to the license.txt file included. #pragma once + #include "core/hle/ipc.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" namespace IPC { diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 6a7af93a9..1d24401b1 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" // 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 diff --git a/src/core/hle/kernel/client_port.cpp b/src/core/hle/kernel/client_port.cpp index 03ffdece1..ce5d94e99 100644 --- a/src/core/hle/kernel/client_port.cpp +++ b/src/core/hle/kernel/client_port.cpp @@ -6,6 +6,7 @@ #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" @@ -25,20 +26,17 @@ ResultVal<SharedPtr<ClientSession>> ClientPort::Connect() { active_sessions++; // Create a new session pair, let the created sessions inherit the parent port's HLE handler. - auto sessions = - ServerSession::CreateSessionPair(server_port->GetName(), server_port->hle_handler, this); - auto client_session = std::get<SharedPtr<ClientSession>>(sessions); - auto server_session = std::get<SharedPtr<ServerSession>>(sessions); + auto sessions = ServerSession::CreateSessionPair(server_port->GetName(), this); if (server_port->hle_handler) - server_port->hle_handler->ClientConnected(server_session); + server_port->hle_handler->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); else - server_port->pending_sessions.push_back(std::move(server_session)); + server_port->pending_sessions.push_back(std::get<SharedPtr<ServerSession>>(sessions)); // Wake the threads waiting on the ServerPort server_port->WakeupAllWaitingThreads(); - return MakeResult<SharedPtr<ClientSession>>(std::move(client_session)); + return MakeResult(std::get<SharedPtr<ClientSession>>(sessions)); } } // namespace diff --git a/src/core/hle/kernel/client_port.h b/src/core/hle/kernel/client_port.h index 511490c7c..8f7d6ac44 100644 --- a/src/core/hle/kernel/client_port.h +++ b/src/core/hle/kernel/client_port.h @@ -7,6 +7,7 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp index 783b1c061..6bc49ff64 100644 --- a/src/core/hle/kernel/client_session.cpp +++ b/src/core/hle/kernel/client_session.cpp @@ -5,6 +5,8 @@ #include "common/assert.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/server_session.h" namespace Kernel { diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h index 9f3adb72b..2de379c09 100644 --- a/src/core/hle/kernel/client_session.h +++ b/src/core/hle/kernel/client_session.h @@ -6,10 +6,9 @@ #include <memory> #include <string> - #include "common/common_types.h" - #include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index 3e3673508..cc41abb85 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp new file mode 100644 index 000000000..c7322d883 --- /dev/null +++ b/src/core/hle/kernel/handle_table.cpp @@ -0,0 +1,97 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <utility> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +HandleTable g_handle_table; + +HandleTable::HandleTable() { + next_generation = 1; + Clear(); +} + +ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { + DEBUG_ASSERT(obj != nullptr); + + u16 slot = next_free_slot; + if (slot >= generations.size()) { + LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); + return ERR_OUT_OF_HANDLES; + } + next_free_slot = generations[slot]; + + u16 generation = next_generation++; + + // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. + // CTR-OS doesn't use generation 0, so skip straight to 1. + if (next_generation >= (1 << 15)) + next_generation = 1; + + generations[slot] = generation; + objects[slot] = std::move(obj); + + Handle handle = generation | (slot << 15); + return MakeResult<Handle>(handle); +} + +ResultVal<Handle> HandleTable::Duplicate(Handle handle) { + SharedPtr<Object> object = GetGeneric(handle); + if (object == nullptr) { + LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); + return ERR_INVALID_HANDLE; + } + return Create(std::move(object)); +} + +ResultCode HandleTable::Close(Handle handle) { + if (!IsValid(handle)) + return ERR_INVALID_HANDLE; + + u16 slot = GetSlot(handle); + + objects[slot] = nullptr; + + generations[slot] = next_free_slot; + next_free_slot = slot; + return RESULT_SUCCESS; +} + +bool HandleTable::IsValid(Handle handle) const { + size_t slot = GetSlot(handle); + u16 generation = GetGeneration(handle); + + return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; +} + +SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { + if (handle == CurrentThread) { + return GetCurrentThread(); + } else if (handle == CurrentProcess) { + return g_current_process; + } + + if (!IsValid(handle)) { + return nullptr; + } + return objects[GetSlot(handle)]; +} + +void HandleTable::Clear() { + for (u16 i = 0; i < MAX_COUNT; ++i) { + generations[i] = i + 1; + objects[i] = nullptr; + } + next_free_slot = 0; +} + +} // namespace diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h new file mode 100644 index 000000000..d6aaefbf7 --- /dev/null +++ b/src/core/hle/kernel/handle_table.h @@ -0,0 +1,126 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <cstddef> +#include "common/common_types.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" + +namespace Kernel { + +enum KernelHandle : Handle { + CurrentThread = 0xFFFF8000, + CurrentProcess = 0xFFFF8001, +}; + +/** + * This class allows the creation of Handles, which are references to objects that can be tested + * for validity and looked up. Here they are used to pass references to kernel objects to/from the + * emulated process. it has been designed so that it follows the same handle format and has + * approximately the same restrictions as the handle manager in the CTR-OS. + * + * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). + * The slot index is used to index into the arrays in this class to access the data corresponding + * to the Handle. + * + * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter + * is kept and incremented every time a Handle is created. This is the Handle's "generation". The + * value of the counter is stored into the Handle as well as in the handle table (in the + * "generations" array). When looking up a handle, the Handle's generation must match with the + * value stored on the class, otherwise the Handle is considered invalid. + * + * To find free slots when allocating a Handle without needing to scan the entire object array, the + * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. + * When a Handle is created, an index is popped off the list and used for the new Handle. When it + * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is + * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been + * verified and isn't likely to cause any problems. + */ +class HandleTable final : NonCopyable { +public: + HandleTable(); + + /** + * Allocates a handle for the given object. + * @return The created Handle or one of the following errors: + * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. + */ + ResultVal<Handle> Create(SharedPtr<Object> obj); + + /** + * Returns a new handle that points to the same object as the passed in handle. + * @return The duplicated Handle or one of the following errors: + * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. + * - Any errors returned by `Create()`. + */ + ResultVal<Handle> Duplicate(Handle handle); + + /** + * Closes a handle, removing it from the table and decreasing the object's ref-count. + * @return `RESULT_SUCCESS` or one of the following errors: + * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. + */ + ResultCode Close(Handle handle); + + /// Checks if a handle is valid and points to an existing object. + bool IsValid(Handle handle) const; + + /** + * Looks up a handle. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. + */ + SharedPtr<Object> GetGeneric(Handle handle) const; + + /** + * Looks up a handle while verifying its type. + * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its + * type differs from the requested one. + */ + template <class T> + SharedPtr<T> Get(Handle handle) const { + return DynamicObjectCast<T>(GetGeneric(handle)); + } + + /// Closes all handles held in this table. + void Clear(); + +private: + /** + * This is the maximum limit of handles allowed per process in CTR-OS. It can be further + * reduced by ExHeader values, but this is not emulated here. + */ + static const size_t MAX_COUNT = 4096; + + static u16 GetSlot(Handle handle) { + return handle >> 15; + } + static u16 GetGeneration(Handle handle) { + return handle & 0x7FFF; + } + + /// Stores the Object referenced by the handle or null if the slot is empty. + std::array<SharedPtr<Object>, MAX_COUNT> objects; + + /** + * The value of `next_generation` when the handle was created, used to check for validity. For + * empty slots, contains the index of the next free slot in the list. + */ + std::array<u16, MAX_COUNT> generations; + + /** + * Global counter of the number of created handles. Stored in `generations` when a handle is + * created, and wraps around to 1 when it hits 0x8000. + */ + u16 next_generation; + + /// Head of the free slots linked list. + u16 next_free_slot; +}; + +extern HandleTable g_handle_table; + +} // namespace diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp new file mode 100644 index 000000000..0922b3f47 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -0,0 +1,24 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <boost/range/algorithm_ext/erase.hpp> +#include "common/assert.h" +#include "common/common_types.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/server_session.h" + +namespace Kernel { + +void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) { + server_session->SetHleHandler(shared_from_this()); + connected_sessions.push_back(server_session); +} + +void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_session) { + server_session->SetHleHandler(nullptr); + boost::range::remove_erase(connected_sessions, server_session); +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h new file mode 100644 index 000000000..14f682f44 --- /dev/null +++ b/src/core/hle/kernel/hle_ipc.h @@ -0,0 +1,53 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <memory> +#include <vector> +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class ServerSession; + +/** + * Interface implemented by HLE Session handlers. + * This can be provided to a ServerSession in order to hook into several relevant events + * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. + */ +class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> { +public: + /** + * Handles a sync request from the emulated application. + * @param server_session The ServerSession that was triggered for this sync request, + * it should be used to differentiate which client (As in ClientSession) we're answering to. + * TODO(Subv): Use a wrapper structure to hold all the information relevant to + * this request (ServerSession, Originator thread, Translated command buffer, etc). + * @returns ResultCode the result code of the translate operation. + */ + virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0; + + /** + * Signals that a client has just connected to this HLE handler and keeps the + * associated ServerSession alive for the duration of the connection. + * @param server_session Owning pointer to the ServerSession associated with the connection. + */ + void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); + + /** + * Signals that a client has just disconnected from this HLE handler and releases the + * associated ServerSession. + * @param server_session ServerSession associated with the connection. + */ + void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); + +protected: + /// List of sessions that are connected to this handler. + /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list + // for the duration of the connection. + std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions; +}; + +} // namespace Kernel diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 7f84e01aa..7470a97ca 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -2,11 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <algorithm> -#include "common/assert.h" -#include "common/logging/log.h" #include "core/hle/config_mem.h" -#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" @@ -18,165 +15,6 @@ namespace Kernel { unsigned int Object::next_object_id; -HandleTable g_handle_table; - -void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { - auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); - if (itr == waiting_threads.end()) - waiting_threads.push_back(std::move(thread)); -} - -void WaitObject::RemoveWaitingThread(Thread* thread) { - auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); - // If a thread passed multiple handles to the same object, - // the kernel might attempt to remove the thread from the object's - // waiting threads list multiple times. - if (itr != waiting_threads.end()) - waiting_threads.erase(itr); -} - -SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { - Thread* candidate = nullptr; - s32 candidate_priority = THREADPRIO_LOWEST + 1; - - for (const auto& thread : waiting_threads) { - // The list of waiting threads must not contain threads that are not waiting to be awakened. - ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || - thread->status == THREADSTATUS_WAIT_SYNCH_ALL, - "Inconsistent thread statuses in waiting_threads"); - - if (thread->current_priority >= candidate_priority) - continue; - - if (ShouldWait(thread.get())) - continue; - - // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or - // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. - bool ready_to_run = true; - if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { - ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), - [&thread](const SharedPtr<WaitObject>& object) { - return object->ShouldWait(thread.get()); - }); - } - - if (ready_to_run) { - candidate = thread.get(); - candidate_priority = thread->current_priority; - } - } - - return candidate; -} - -void WaitObject::WakeupAllWaitingThreads() { - while (auto thread = GetHighestPriorityReadyThread()) { - if (!thread->IsSleepingOnWaitAll()) { - Acquire(thread.get()); - // Set the output index of the WaitSynchronizationN call to the index of this object. - if (thread->wait_set_output) { - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); - thread->wait_set_output = false; - } - } else { - for (auto& object : thread->wait_objects) { - object->Acquire(thread.get()); - } - // Note: This case doesn't update the output index of WaitSynchronizationN. - } - - for (auto& object : thread->wait_objects) - object->RemoveWaitingThread(thread.get()); - thread->wait_objects.clear(); - - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); - thread->ResumeFromWait(); - } -} - -const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { - return waiting_threads; -} - -HandleTable::HandleTable() { - next_generation = 1; - Clear(); -} - -ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { - DEBUG_ASSERT(obj != nullptr); - - u16 slot = next_free_slot; - if (slot >= generations.size()) { - LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use."); - return ERR_OUT_OF_HANDLES; - } - next_free_slot = generations[slot]; - - u16 generation = next_generation++; - - // Overflow count so it fits in the 15 bits dedicated to the generation in the handle. - // CTR-OS doesn't use generation 0, so skip straight to 1. - if (next_generation >= (1 << 15)) - next_generation = 1; - - generations[slot] = generation; - objects[slot] = std::move(obj); - - Handle handle = generation | (slot << 15); - return MakeResult<Handle>(handle); -} - -ResultVal<Handle> HandleTable::Duplicate(Handle handle) { - SharedPtr<Object> object = GetGeneric(handle); - if (object == nullptr) { - LOG_ERROR(Kernel, "Tried to duplicate invalid handle: %08X", handle); - return ERR_INVALID_HANDLE; - } - return Create(std::move(object)); -} - -ResultCode HandleTable::Close(Handle handle) { - if (!IsValid(handle)) - return ERR_INVALID_HANDLE; - - u16 slot = GetSlot(handle); - - objects[slot] = nullptr; - - generations[slot] = next_free_slot; - next_free_slot = slot; - return RESULT_SUCCESS; -} - -bool HandleTable::IsValid(Handle handle) const { - size_t slot = GetSlot(handle); - u16 generation = GetGeneration(handle); - - return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation; -} - -SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const { - if (handle == CurrentThread) { - return GetCurrentThread(); - } else if (handle == CurrentProcess) { - return g_current_process; - } - - if (!IsValid(handle)) { - return nullptr; - } - return objects[GetSlot(handle)]; -} - -void HandleTable::Clear() { - for (u16 i = 0; i < MAX_COUNT; ++i) { - generations[i] = i + 1; - objects[i] = nullptr; - } - next_free_slot = 0; -} /// Initialize the kernel void Init(u32 system_mode) { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 94f2025a0..9cf288b08 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -4,26 +4,16 @@ #pragma once -#include <algorithm> -#include <array> #include <cstddef> #include <string> -#include <vector> +#include <utility> #include <boost/smart_ptr/intrusive_ptr.hpp> #include "common/common_types.h" -#include "core/hle/result.h" namespace Kernel { using Handle = u32; -class Thread; - -enum KernelHandle : Handle { - CurrentThread = 0xFFFF8000, - CurrentProcess = 0xFFFF8001, -}; - enum class HandleType : u32 { Unknown, Event, @@ -121,170 +111,17 @@ inline void intrusive_ptr_release(Object* object) { template <typename T> using SharedPtr = boost::intrusive_ptr<T>; -/// Class that represents a Kernel object that a thread can be waiting on -class WaitObject : public Object { -public: - /** - * Check if the specified thread should wait until the object is available - * @param thread The thread about which we're deciding. - * @return True if the current thread should wait due to this object being unavailable - */ - virtual bool ShouldWait(Thread* thread) const = 0; - - /// Acquire/lock the object for the specified thread if it is available - virtual void Acquire(Thread* thread) = 0; - - /** - * Add a thread to wait on this object - * @param thread Pointer to thread to add - */ - virtual void AddWaitingThread(SharedPtr<Thread> thread); - - /** - * Removes a thread from waiting on this object (e.g. if it was resumed already) - * @param thread Pointer to thread to remove - */ - virtual void RemoveWaitingThread(Thread* thread); - - /** - * Wake up all threads waiting on this object that can be awoken, in priority order, - * and set the synchronization result and output of the thread. - */ - virtual void WakeupAllWaitingThreads(); - - /// Obtains the highest priority thread that is ready to run from this object's waiting list. - SharedPtr<Thread> GetHighestPriorityReadyThread(); - - /// Get a const reference to the waiting threads list for debug use - const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; - -private: - /// Threads waiting for this object to become available - std::vector<SharedPtr<Thread>> waiting_threads; -}; - /** - * This class allows the creation of Handles, which are references to objects that can be tested - * for validity and looked up. Here they are used to pass references to kernel objects to/from the - * emulated process. it has been designed so that it follows the same handle format and has - * approximately the same restrictions as the handle manager in the CTR-OS. - * - * Handles contain two sub-fields: a slot index (bits 31:15) and a generation value (bits 14:0). - * The slot index is used to index into the arrays in this class to access the data corresponding - * to the Handle. - * - * To prevent accidental use of a freed Handle whose slot has already been reused, a global counter - * is kept and incremented every time a Handle is created. This is the Handle's "generation". The - * value of the counter is stored into the Handle as well as in the handle table (in the - * "generations" array). When looking up a handle, the Handle's generation must match with the - * value stored on the class, otherwise the Handle is considered invalid. - * - * To find free slots when allocating a Handle without needing to scan the entire object array, the - * generations field of unallocated slots is re-purposed as a linked list of indices to free slots. - * When a Handle is created, an index is popped off the list and used for the new Handle. When it - * is destroyed, it is again pushed onto the list to be re-used by the next allocation. It is - * likely that this allocation strategy differs from the one used in CTR-OS, but this hasn't been - * verified and isn't likely to cause any problems. + * Attempts to downcast the given Object pointer to a pointer to T. + * @return Derived pointer to the object, or `nullptr` if `object` isn't of type T. */ -class HandleTable final : NonCopyable { -public: - HandleTable(); - - /** - * Allocates a handle for the given object. - * @return The created Handle or one of the following errors: - * - `ERR_OUT_OF_HANDLES`: the maximum number of handles has been exceeded. - */ - ResultVal<Handle> Create(SharedPtr<Object> obj); - - /** - * Returns a new handle that points to the same object as the passed in handle. - * @return The duplicated Handle or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - * - Any errors returned by `Create()`. - */ - ResultVal<Handle> Duplicate(Handle handle); - - /** - * Closes a handle, removing it from the table and decreasing the object's ref-count. - * @return `RESULT_SUCCESS` or one of the following errors: - * - `ERR_INVALID_HANDLE`: an invalid handle was passed in. - */ - ResultCode Close(Handle handle); - - /// Checks if a handle is valid and points to an existing object. - bool IsValid(Handle handle) const; - - /** - * Looks up a handle. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid. - */ - SharedPtr<Object> GetGeneric(Handle handle) const; - - /** - * Looks up a handle while verifying its type. - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or its - * type differs from the handle type `T::HANDLE_TYPE`. - */ - template <class T> - SharedPtr<T> Get(Handle handle) const { - SharedPtr<Object> object = GetGeneric(handle); - if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { - return boost::static_pointer_cast<T>(std::move(object)); - } - return nullptr; - } - - /** - * Looks up a handle while verifying that it is an object that a thread can wait on - * @return Pointer to the looked-up object, or `nullptr` if the handle is not valid or it is - * not a waitable object. - */ - SharedPtr<WaitObject> GetWaitObject(Handle handle) const { - SharedPtr<Object> object = GetGeneric(handle); - if (object != nullptr && object->IsWaitable()) { - return boost::static_pointer_cast<WaitObject>(std::move(object)); - } - return nullptr; - } - - /// Closes all handles held in this table. - void Clear(); - -private: - /** - * This is the maximum limit of handles allowed per process in CTR-OS. It can be further - * reduced by ExHeader values, but this is not emulated here. - */ - static const size_t MAX_COUNT = 4096; - - static u16 GetSlot(Handle handle) { - return handle >> 15; - } - static u16 GetGeneration(Handle handle) { - return handle & 0x7FFF; +template <typename T> +inline SharedPtr<T> DynamicObjectCast(SharedPtr<Object> object) { + if (object != nullptr && object->GetHandleType() == T::HANDLE_TYPE) { + return boost::static_pointer_cast<T>(std::move(object)); } - - /// Stores the Object referenced by the handle or null if the slot is empty. - std::array<SharedPtr<Object>, MAX_COUNT> objects; - - /** - * The value of `next_generation` when the handle was created, used to check for validity. For - * empty slots, contains the index of the next free slot in the list. - */ - std::array<u16, MAX_COUNT> generations; - - /** - * Global counter of the number of created handles. Stored in `generations` when a handle is - * created, and wraps around to 1 when it hits 0x8000. - */ - u16 next_generation; - - /// Head of the free slots linked list. - u16 next_free_slot; -}; - -extern HandleTable g_handle_table; + return nullptr; +} /// Initialize the kernel with the specified system mode. void Init(u32 system_mode); diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 8250a90b5..804f23b1c 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include <map> #include <memory> diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index c57adf400..bacacd690 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -7,6 +7,7 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index 3f51bc5de..a8f10a3ee 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -3,6 +3,7 @@ // Refer to the license.txt file included. #include <cstring> +#include "common/assert.h" #include "common/logging/log.h" #include "core/hle/kernel/resource_limit.h" diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index cde94f7cc..7b0cacf2e 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -8,6 +8,8 @@ #include <string> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" +#include "core/hle/result.h" namespace Kernel { diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index fd3bbbcad..4d20c39a1 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -24,14 +24,12 @@ void ServerPort::Acquire(Thread* thread) { } std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> ServerPort::CreatePortPair( - u32 max_sessions, std::string name, - std::shared_ptr<Service::SessionRequestHandler> hle_handler) { + u32 max_sessions, std::string name) { SharedPtr<ServerPort> server_port(new ServerPort); SharedPtr<ClientPort> client_port(new ClientPort); server_port->name = name + "_Server"; - server_port->hle_handler = std::move(hle_handler); client_port->name = name + "_Client"; client_port->server_port = server_port; client_port->max_sessions = max_sessions; diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index 6f8bdb6a9..f1419cd46 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -9,28 +9,24 @@ #include <tuple> #include "common/common_types.h" #include "core/hle/kernel/kernel.h" - -namespace Service { -class SessionRequestHandler; -} +#include "core/hle/kernel/wait_object.h" namespace Kernel { class ClientPort; +class SessionRequestHandler; class ServerPort final : public WaitObject { public: /** * Creates a pair of ServerPort and an associated ClientPort. + * * @param max_sessions Maximum number of sessions to the port * @param name Optional name of the ports - * @param hle_handler Optional HLE handler template for the port, - * ServerSessions crated from this port will inherit a reference to this handler. * @return The created port tuple */ static std::tuple<SharedPtr<ServerPort>, SharedPtr<ClientPort>> CreatePortPair( - u32 max_sessions, std::string name = "UnknownPort", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + u32 max_sessions, std::string name = "UnknownPort"); std::string GetTypeName() const override { return "ServerPort"; @@ -44,6 +40,14 @@ public: return HANDLE_TYPE; } + /** + * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port + * will inherit a reference to this handler. + */ + void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + hle_handler = std::move(hle_handler_); + } + std::string name; ///< Name of port (optional) std::vector<SharedPtr<WaitObject>> @@ -51,7 +55,7 @@ public: /// This session's HLE request handler template (optional) /// ServerSessions created from this port inherit a reference to this handler. - std::shared_ptr<Service::SessionRequestHandler> hle_handler; + std::shared_ptr<SessionRequestHandler> hle_handler; bool ShouldWait(Thread* thread) const override; void Acquire(Thread* thread) override; diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 500b909ab..2dc709bc9 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -4,8 +4,11 @@ #include <tuple> +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/session.h" #include "core/hle/kernel/thread.h" namespace Kernel { @@ -25,16 +28,14 @@ ServerSession::~ServerSession() { parent->server = nullptr; } -ResultVal<SharedPtr<ServerSession>> ServerSession::Create( - std::string name, std::shared_ptr<Service::SessionRequestHandler> hle_handler) { +ResultVal<SharedPtr<ServerSession>> ServerSession::Create(std::string name) { SharedPtr<ServerSession> server_session(new ServerSession); server_session->name = std::move(name); server_session->signaled = false; - server_session->hle_handler = std::move(hle_handler); server_session->parent = nullptr; - return MakeResult<SharedPtr<ServerSession>>(std::move(server_session)); + return MakeResult(std::move(server_session)); } bool ServerSession::ShouldWait(Thread* thread) const { @@ -68,13 +69,9 @@ ResultCode ServerSession::HandleSyncRequest() { return RESULT_SUCCESS; } -ServerSession::SessionPair ServerSession::CreateSessionPair( - const std::string& name, std::shared_ptr<Service::SessionRequestHandler> hle_handler, - SharedPtr<ClientPort> port) { - - auto server_session = - ServerSession::Create(name + "_Server", std::move(hle_handler)).MoveFrom(); - +ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, + SharedPtr<ClientPort> port) { + auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); SharedPtr<ClientSession> client_session(new ClientSession); client_session->name = name + "_Client"; diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c907d487c..28f365b9e 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -10,9 +10,8 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/session.h" -#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" -#include "core/hle/service/service.h" #include "core/memory.h" namespace Kernel { @@ -20,6 +19,8 @@ namespace Kernel { class ClientSession; class ClientPort; class ServerSession; +class SessionRequestHandler; +class Thread; /** * Kernel object representing the server endpoint of an IPC session. Sessions are the basic CTR-OS @@ -49,14 +50,20 @@ public: /** * Creates a pair of ServerSession and an associated ClientSession. * @param name Optional name of the ports. - * @param hle_handler Optional HLE handler for this server session. * @param client_port Optional The ClientPort that spawned this session. * @return The created session tuple */ - static SessionPair CreateSessionPair( - const std::string& name = "Unknown", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr, - SharedPtr<ClientPort> client_port = nullptr); + static SessionPair CreateSessionPair(const std::string& name = "Unknown", + SharedPtr<ClientPort> client_port = nullptr); + + /** + * Sets the HLE handler for the session. This handler will be called to service IPC requests + * instead of the regular IPC machinery. (The regular IPC machinery is currently not + * implemented.) + */ + void SetHleHandler(std::shared_ptr<SessionRequestHandler> hle_handler_) { + hle_handler = std::move(hle_handler_); + } /** * Handle a sync request from the emulated application. @@ -71,7 +78,7 @@ public: std::string name; ///< The name of this session (optional) bool signaled; ///< Whether there's new data available to this ServerSession std::shared_ptr<Session> parent; ///< The parent session, which links to the client endpoint. - std::shared_ptr<Service::SessionRequestHandler> + std::shared_ptr<SessionRequestHandler> hle_handler; ///< This session's HLE request handler (optional) private: @@ -82,12 +89,9 @@ private: * Creates a server session. The server session can have an optional HLE handler, * which will be invoked to handle the IPC requests that this session receives. * @param name Optional name of the server session. - * @param hle_handler Optional HLE handler for this server session. * @return The created server session */ - static ResultVal<SharedPtr<ServerSession>> Create( - std::string name = "Unknown", - std::shared_ptr<Service::SessionRequestHandler> hle_handler = nullptr); + static ResultVal<SharedPtr<ServerSession>> Create(std::string name = "Unknown"); }; /** diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 519ff51a8..75ce626f8 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -15,6 +15,7 @@ #include "core/core.h" #include "core/core_timing.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 7b5169cfc..6a3566f15 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -12,6 +12,7 @@ #include "common/common_types.h" #include "core/arm/arm_interface.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" enum ThreadPriority : s32 { diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index a00c75679..6f2cf3b02 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -6,6 +6,7 @@ #include "common/assert.h" #include "common/logging/log.h" #include "core/core_timing.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index b0f818933..82552372d 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/wait_object.h" namespace Kernel { diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp new file mode 100644 index 000000000..f245eda6c --- /dev/null +++ b/src/core/hle/kernel/wait_object.cpp @@ -0,0 +1,99 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <algorithm> +#include "common/assert.h" +#include "common/logging/log.h" +#include "core/hle/config_mem.h" +#include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/memory.h" +#include "core/hle/kernel/process.h" +#include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/kernel/timer.h" +#include "core/hle/shared_page.h" + +namespace Kernel { + +void WaitObject::AddWaitingThread(SharedPtr<Thread> thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + if (itr == waiting_threads.end()) + waiting_threads.push_back(std::move(thread)); +} + +void WaitObject::RemoveWaitingThread(Thread* thread) { + auto itr = std::find(waiting_threads.begin(), waiting_threads.end(), thread); + // If a thread passed multiple handles to the same object, + // the kernel might attempt to remove the thread from the object's + // waiting threads list multiple times. + if (itr != waiting_threads.end()) + waiting_threads.erase(itr); +} + +SharedPtr<Thread> WaitObject::GetHighestPriorityReadyThread() { + Thread* candidate = nullptr; + s32 candidate_priority = THREADPRIO_LOWEST + 1; + + for (const auto& thread : waiting_threads) { + // The list of waiting threads must not contain threads that are not waiting to be awakened. + ASSERT_MSG(thread->status == THREADSTATUS_WAIT_SYNCH_ANY || + thread->status == THREADSTATUS_WAIT_SYNCH_ALL, + "Inconsistent thread statuses in waiting_threads"); + + if (thread->current_priority >= candidate_priority) + continue; + + if (ShouldWait(thread.get())) + continue; + + // A thread is ready to run if it's either in THREADSTATUS_WAIT_SYNCH_ANY or + // in THREADSTATUS_WAIT_SYNCH_ALL and the rest of the objects it is waiting on are ready. + bool ready_to_run = true; + if (thread->status == THREADSTATUS_WAIT_SYNCH_ALL) { + ready_to_run = std::none_of(thread->wait_objects.begin(), thread->wait_objects.end(), + [&thread](const SharedPtr<WaitObject>& object) { + return object->ShouldWait(thread.get()); + }); + } + + if (ready_to_run) { + candidate = thread.get(); + candidate_priority = thread->current_priority; + } + } + + return candidate; +} + +void WaitObject::WakeupAllWaitingThreads() { + while (auto thread = GetHighestPriorityReadyThread()) { + if (!thread->IsSleepingOnWaitAll()) { + Acquire(thread.get()); + // Set the output index of the WaitSynchronizationN call to the index of this object. + if (thread->wait_set_output) { + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); + thread->wait_set_output = false; + } + } else { + for (auto& object : thread->wait_objects) { + object->Acquire(thread.get()); + } + // Note: This case doesn't update the output index of WaitSynchronizationN. + } + + for (auto& object : thread->wait_objects) + object->RemoveWaitingThread(thread.get()); + thread->wait_objects.clear(); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->ResumeFromWait(); + } +} + +const std::vector<SharedPtr<Thread>>& WaitObject::GetWaitingThreads() const { + return waiting_threads; +} + +} // namespace Kernel diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h new file mode 100644 index 000000000..861578186 --- /dev/null +++ b/src/core/hle/kernel/wait_object.h @@ -0,0 +1,67 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <vector> +#include <boost/smart_ptr/intrusive_ptr.hpp> +#include "common/common_types.h" +#include "core/hle/kernel/kernel.h" + +namespace Kernel { + +class Thread; + +/// Class that represents a Kernel object that a thread can be waiting on +class WaitObject : public Object { +public: + /** + * Check if the specified thread should wait until the object is available + * @param thread The thread about which we're deciding. + * @return True if the current thread should wait due to this object being unavailable + */ + virtual bool ShouldWait(Thread* thread) const = 0; + + /// Acquire/lock the object for the specified thread if it is available + virtual void Acquire(Thread* thread) = 0; + + /** + * Add a thread to wait on this object + * @param thread Pointer to thread to add + */ + virtual void AddWaitingThread(SharedPtr<Thread> thread); + + /** + * Removes a thread from waiting on this object (e.g. if it was resumed already) + * @param thread Pointer to thread to remove + */ + virtual void RemoveWaitingThread(Thread* thread); + + /** + * Wake up all threads waiting on this object that can be awoken, in priority order, + * and set the synchronization result and output of the thread. + */ + virtual void WakeupAllWaitingThreads(); + + /// Obtains the highest priority thread that is ready to run from this object's waiting list. + SharedPtr<Thread> GetHighestPriorityReadyThread(); + + /// Get a const reference to the waiting threads list for debug use + const std::vector<SharedPtr<Thread>>& GetWaitingThreads() const; + +private: + /// Threads waiting for this object to become available + std::vector<SharedPtr<Thread>> waiting_threads; +}; + +// Specialization of DynamicObjectCast for WaitObjects +template <> +inline SharedPtr<WaitObject> DynamicObjectCast<WaitObject>(SharedPtr<Object> object) { + if (object != nullptr && object->IsWaitable()) { + return boost::static_pointer_cast<WaitObject>(std::move(object)); + } + return nullptr; +} + +} // namespace Kernel diff --git a/src/core/hle/result.h b/src/core/hle/result.h index c49650f7d..5f2cdbb96 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -416,6 +416,16 @@ ResultVal<T> MakeResult(Args&&... args) { } /** + * Deducible overload of MakeResult, allowing the template parameter to be ommited if you're just + * copy or move constructing. + */ +template <typename Arg> +ResultVal<std::remove_reference_t<Arg>> MakeResult(Arg&& arg) { + return ResultVal<std::remove_reference_t<Arg>>::WithCode(RESULT_SUCCESS, + std::forward<Arg>(arg)); +} + +/** * Check for the success of `source` (which must evaluate to a ResultVal). If it succeeds, unwraps * the contained value and assigns it to `target`, which can be either an l-value expression or a * variable declaration. If it fails the return code is returned from the current function. Thus it @@ -426,3 +436,12 @@ ResultVal<T> MakeResult(Args&&... args) { if (CONCAT2(check_result_L, __LINE__).Failed()) \ return CONCAT2(check_result_L, __LINE__).Code(); \ target = std::move(*CONCAT2(check_result_L, __LINE__)) + +/** + * Analogous to CASCADE_RESULT, but for a bare ResultCode. The code will be propagated if + * non-success, or discarded otherwise. + */ +#define CASCADE_CODE(source) \ + auto CONCAT2(check_result_L, __LINE__) = source; \ + if (CONCAT2(check_result_L, __LINE__).IsError()) \ + return CONCAT2(check_result_L, __LINE__); diff --git a/src/core/hle/service/ac/ac.cpp b/src/core/hle/service/ac/ac.cpp index aa270a2c3..e3dd23949 100644 --- a/src/core/hle/service/ac/ac.cpp +++ b/src/core/hle/service/ac/ac.cpp @@ -4,11 +4,16 @@ #include <array> +#include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/result.h" #include "core/hle/service/ac/ac.h" #include "core/hle/service/ac/ac_i.h" #include "core/hle/service/ac/ac_u.h" +#include "core/memory.h" namespace Service { namespace AC { diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index d344a622f..961305e9f 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp @@ -2,8 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <cinttypes> +#include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/result.h" #include "core/hle/service/am/am.h" #include "core/hle/service/am/am_app.h" #include "core/hle/service/am/am_net.h" @@ -176,8 +180,6 @@ void GetTicketList(Service::Interface* self) { } void Init() { - using namespace Kernel; - AddService(new AM_APP_Interface); AddService(new AM_NET_Interface); AddService(new AM_SYS_Interface); diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 366d1eacf..4c587e3c8 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -5,6 +5,7 @@ #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" +#include "core/core.h" #include "core/hle/applets/applet.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" @@ -74,6 +75,7 @@ void GetSharedFont(Service::Interface* self) { LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); rb.Push<u32>(-1); // TODO: Find the right error code rb.Skip(1 + 2, true); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); return; } diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index e63b61450..ee80926d2 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -4,6 +4,8 @@ #pragma once +#include <vector> +#include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" #include "core/hle/kernel/kernel.h" diff --git a/src/core/hle/service/boss/boss.cpp b/src/core/hle/service/boss/boss.cpp index 91056189a..2bba3aff6 100644 --- a/src/core/hle/service/boss/boss.cpp +++ b/src/core/hle/service/boss/boss.cpp @@ -3,6 +3,9 @@ // Refer to the license.txt file included. #include <cinttypes> +#include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/result.h" #include "core/hle/service/boss/boss.h" #include "core/hle/service/boss/boss_p.h" #include "core/hle/service/boss/boss_u.h" diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 95665e754..7394c844f 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -11,13 +11,17 @@ #include "common/logging/log.h" #include "core/core_timing.h" #include "core/frontend/camera/factory.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" +#include "core/hle/result.h" #include "core/hle/service/cam/cam.h" #include "core/hle/service/cam/cam_c.h" #include "core/hle/service/cam/cam_q.h" #include "core/hle/service/cam/cam_s.h" #include "core/hle/service/cam/cam_u.h" #include "core/hle/service/service.h" +#include "core/memory.h" #include "core/settings.h" namespace Service { @@ -55,7 +59,7 @@ struct PortConfig { u16 x1; // x-coordinate of ending position for trimming u16 y1; // y-coordinate of ending position for trimming - u32 transfer_bytes; + u16 transfer_bytes; Kernel::SharedPtr<Kernel::Event> completion_event; Kernel::SharedPtr<Kernel::Event> buffer_error_interrupt_event; @@ -225,8 +229,7 @@ static void ActivatePort(int port_id, int camera_id) { template <int max_index> class CommandParamBitSet : public BitSet8 { public: - explicit CommandParamBitSet(u32 command_param) - : BitSet8(static_cast<u8>(command_param & 0xFF)) {} + explicit CommandParamBitSet(u8 command_param) : BitSet8(command_param) {} bool IsValid() const { return m_val < (1 << max_index); @@ -244,9 +247,10 @@ using CameraSet = CommandParamBitSet<3>; } // namespace void StartCapture(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - const PortSet port_select(cmd_buff[1]); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { @@ -267,21 +271,20 @@ void StartCapture(Service::Interface* self) { LOG_WARNING(Service_CAM, "port %u already started", i); } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void StopCapture(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - const PortSet port_select(cmd_buff[1]); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { @@ -293,21 +296,20 @@ void StopCapture(Service::Interface* self) { LOG_WARNING(Service_CAM, "port %u already stopped", i); } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x2, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void IsBusy(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - const PortSet port_select(cmd_buff[1]); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsValid()) { bool is_busy = true; @@ -315,80 +317,74 @@ void IsBusy(Service::Interface* self) { for (int i : port_select) { is_busy &= ports[i].is_busy; } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = is_busy ? 1 : 0; + rb.Push(RESULT_SUCCESS); + rb.Push(is_busy); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0x3, 2, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void ClearBuffer(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x04, 1, 0); + const PortSet port_select(rp.Pop<u8>()); - cmd_buff[0] = IPC::MakeHeader(0x4, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void GetVsyncInterruptEvent(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles( + Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[2] = 0; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.PushCopyHandles(0); } - cmd_buff[0] = IPC::MakeHeader(0x5, 1, 2); - LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void GetBufferErrorInterruptEvent(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = - Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles( + Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[2] = 0; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.PushCopyHandles(0); } LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u", port_select.m_val); } void SetReceiving(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const VAddr dest = cmd_buff[1]; - const PortSet port_select(cmd_buff[2]); - const u32 image_size = cmd_buff[3]; - const u32 trans_unit = cmd_buff[4] & 0xFFFF; - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 4, 2); + const VAddr dest = rp.Pop<u32>(); + const PortSet port_select(rp.Pop<u8>()); + const u32 image_size = rp.Pop<u32>(); + const u16 trans_unit = rp.Pop<u16>(); + rp.PopHandle(); // Handle to destination process. not used + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); if (port_select.IsSingle()) { int port_id = *port_select.begin(); PortConfig& port = ports[port_id]; @@ -403,149 +399,145 @@ void SetReceiving(Service::Interface* self) { port.is_pending_receiving = true; } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(port.completion_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.PushCopyHandles(0); } - cmd_buff[0] = IPC::MakeHeader(0x7, 1, 2); - LOG_DEBUG(Service_CAM, "called, addr=0x%X, port_select=%u, image_size=%u, trans_unit=%u", dest, port_select.m_val, image_size, trans_unit); } void IsFinishedReceiving(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = (ports[port].is_receiving || ports[port].is_pending_receiving) ? 0 : 1; + bool is_busy = ports[port].is_receiving || ports[port].is_pending_receiving; + rb.Push(RESULT_SUCCESS); + rb.Push(!is_busy); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0x8, 2, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTransferLines(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u32 transfer_lines = cmd_buff[2] & 0xFFFF; - const u32 width = cmd_buff[3] & 0xFFFF; - const u32 height = cmd_buff[4] & 0xFFFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x09, 4, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 transfer_lines = rp.Pop<u16>(); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].transfer_bytes = transfer_lines * width * 2; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x9, 1, 0); - LOG_WARNING(Service_CAM, "(STUBBED) called, port_select=%u, lines=%u, width=%u, height=%u", port_select.m_val, transfer_lines, width, height); } void GetMaxLines(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0A, 2, 0); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); - const u32 width = cmd_buff[1] & 0xFFFF; - const u32 height = cmd_buff[2] & 0xFFFF; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 constexpr u32 MIN_TRANSFER_UNIT = 256; constexpr u32 MAX_BUFFER_SIZE = 2560; if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { - cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + rb.Push(ERROR_OUT_OF_RANGE); + rb.Skip(1, false); } else { u32 lines = MAX_BUFFER_SIZE / width; if (lines > height) { lines = height; } - cmd_buff[1] = RESULT_SUCCESS.raw; + ResultCode result = RESULT_SUCCESS; while (height % lines != 0 || (lines * width * 2 % MIN_TRANSFER_UNIT != 0)) { --lines; if (lines == 0) { - cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + result = ERROR_OUT_OF_RANGE; break; } } - cmd_buff[2] = lines; + rb.Push(result); + rb.Push(lines); } - cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); - LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); } void SetTransferBytes(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u32 transfer_bytes = cmd_buff[2] & 0xFFFF; - const u32 width = cmd_buff[3] & 0xFFFF; - const u32 height = cmd_buff[4] & 0xFFFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0B, 4, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 transfer_bytes = rp.Pop<u16>(); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].transfer_bytes = transfer_bytes; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0xB, 1, 0); - LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u, bytes=%u, width=%u, height=%u", port_select.m_val, transfer_bytes, width, height); } void GetTransferBytes(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0C, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = ports[port].transfer_bytes; + rb.Push(RESULT_SUCCESS); + rb.Push(ports[port].transfer_bytes); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0xC, 2, 0); - LOG_WARNING(Service_CAM, "(STUBBED)called, port_select=%u", port_select.m_val); } void GetMaxBytes(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 2, 0); + const u16 width = rp.Pop<u16>(); + const u16 height = rp.Pop<u16>(); - const u32 width = cmd_buff[1] & 0xFFFF; - const u32 height = cmd_buff[2] & 0xFFFF; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); // Note: the result of the algorithm below are hwtested with width < 640 and with height < 480 constexpr u32 MIN_TRANSFER_UNIT = 256; constexpr u32 MAX_BUFFER_SIZE = 2560; if (width * height * 2 % MIN_TRANSFER_UNIT != 0) { - cmd_buff[1] = ERROR_OUT_OF_RANGE.raw; + rb.Push(ERROR_OUT_OF_RANGE); + rb.Skip(1, false); } else { u32 bytes = MAX_BUFFER_SIZE; @@ -553,63 +545,59 @@ void GetMaxBytes(Service::Interface* self) { bytes -= MIN_TRANSFER_UNIT; } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = bytes; + rb.Push(RESULT_SUCCESS); + rb.Push(bytes); } - cmd_buff[0] = IPC::MakeHeader(0xD, 2, 0); LOG_DEBUG(Service_CAM, "called, width=%u, height=%u", width, height); } void SetTrimming(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const bool trim = (cmd_buff[2] & 0xFF) != 0; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0E, 2, 0); + const PortSet port_select(rp.Pop<u8>()); + const bool trim = rp.Pop<bool>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].is_trimming = trim; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0xE, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u, trim=%d", port_select.m_val, trim); } void IsTrimming(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = ports[port].is_trimming; + rb.Push(RESULT_SUCCESS); + rb.Push(ports[port].is_trimming); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(1, false); } - cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTrimmingParams(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u16 x0 = static_cast<u16>(cmd_buff[2] & 0xFFFF); - const u16 y0 = static_cast<u16>(cmd_buff[3] & 0xFFFF); - const u16 x1 = static_cast<u16>(cmd_buff[4] & 0xFFFF); - const u16 y1 = static_cast<u16>(cmd_buff[5] & 0xFFFF); - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x10, 5, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 x0 = rp.Pop<u16>(); + const u16 y0 = rp.Pop<u16>(); + const u16 x1 = rp.Pop<u16>(); + const u16 y1 = rp.Pop<u16>(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].x0 = x0; @@ -617,49 +605,46 @@ void SetTrimmingParams(Service::Interface* self) { ports[i].x1 = x1; ports[i].y1 = y1; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u, x0=%u, y0=%u, x1=%u, y1=%u", port_select.m_val, x0, y0, x1, y1); } void GetTrimmingParams(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x11, 1, 0); + const PortSet port_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); if (port_select.IsSingle()) { int port = *port_select.begin(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = ports[port].x0; - cmd_buff[3] = ports[port].y0; - cmd_buff[4] = ports[port].x1; - cmd_buff[5] = ports[port].y1; + rb.Push(RESULT_SUCCESS); + rb.Push(ports[port].x0); + rb.Push(ports[port].y0); + rb.Push(ports[port].x1); + rb.Push(ports[port].y1); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); + rb.Skip(4, false); } - cmd_buff[0] = IPC::MakeHeader(0x11, 5, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u", port_select.m_val); } void SetTrimmingParamsCenter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const PortSet port_select(cmd_buff[1]); - const u16 trim_w = static_cast<u16>(cmd_buff[2] & 0xFFFF); - const u16 trim_h = static_cast<u16>(cmd_buff[3] & 0xFFFF); - const u16 cam_w = static_cast<u16>(cmd_buff[4] & 0xFFFF); - const u16 cam_h = static_cast<u16>(cmd_buff[5] & 0xFFFF); - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 5, 0); + const PortSet port_select(rp.Pop<u8>()); + const u16 trim_w = rp.Pop<u16>(); + const u16 trim_h = rp.Pop<u16>(); + const u16 cam_w = rp.Pop<u16>(); + const u16 cam_h = rp.Pop<u16>(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (port_select.IsValid()) { for (int i : port_select) { ports[i].x0 = (cam_w - trim_w) / 2; @@ -667,23 +652,21 @@ void SetTrimmingParamsCenter(Service::Interface* self) { ports[i].x1 = ports[i].x0 + trim_w; ports[i].y1 = ports[i].y0 + trim_h; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x12, 1, 0); - LOG_DEBUG(Service_CAM, "called, port_select=%u, trim_w=%u, trim_h=%u, cam_w=%u, cam_h=%u", port_select.m_val, trim_w, trim_h, cam_w, cam_h); } void Activate(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x13, 1, 0); + const CameraSet camera_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid()) { if (camera_select.m_val == 0) { // deactive all for (int i = 0; i < 2; ++i) { @@ -694,10 +677,10 @@ void Activate(Service::Interface* self) { } ports[i].is_active = false; } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else if (camera_select[0] && camera_select[1]) { LOG_ERROR(Service_CAM, "camera 0 and 1 can't be both activated"); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } else { if (camera_select[0]) { ActivatePort(0, 0); @@ -708,24 +691,22 @@ void Activate(Service::Interface* self) { if (camera_select[2]) { ActivatePort(1, 2); } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x13, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u", camera_select.m_val); } void SwitchContext(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const ContextSet context_select(cmd_buff[2]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x14, 2, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsSingle()) { int context = *context_select.begin(); for (int camera : camera_select) { @@ -736,26 +717,24 @@ void SwitchContext(Service::Interface* self) { cameras[camera].impl->SetFormat(context_config.format); cameras[camera].impl->SetResolution(context_config.resolution); } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x14, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); } void FlipImage(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const Flip flip = static_cast<Flip>(cmd_buff[2] & 0xFF); - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const Flip flip = static_cast<Flip>(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -765,32 +744,30 @@ void FlipImage(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1D, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, flip=%d, context_select=%u", camera_select.m_val, static_cast<int>(flip), context_select.m_val); } void SetDetailSize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 8, 0); + const CameraSet camera_select(rp.Pop<u8>()); Resolution resolution; - resolution.width = static_cast<u16>(cmd_buff[2] & 0xFFFF); - resolution.height = static_cast<u16>(cmd_buff[3] & 0xFFFF); - resolution.crop_x0 = static_cast<u16>(cmd_buff[4] & 0xFFFF); - resolution.crop_y0 = static_cast<u16>(cmd_buff[5] & 0xFFFF); - resolution.crop_x1 = static_cast<u16>(cmd_buff[6] & 0xFFFF); - resolution.crop_y1 = static_cast<u16>(cmd_buff[7] & 0xFFFF); - const ContextSet context_select(cmd_buff[8]); - + resolution.width = rp.Pop<u16>(); + resolution.height = rp.Pop<u16>(); + resolution.crop_x0 = rp.Pop<u16>(); + resolution.crop_y0 = rp.Pop<u16>(); + resolution.crop_x1 = rp.Pop<u16>(); + resolution.crop_y1 = rp.Pop<u16>(); + const ContextSet context_select(rp.Pop<u8>()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -800,15 +777,13 @@ void SetDetailSize(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1E, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, width=%u, height=%u, crop_x0=%u, crop_y0=%u, " "crop_x1=%u, crop_y1=%u, context_select=%u", camera_select.m_val, resolution.width, resolution.height, resolution.crop_x0, @@ -816,12 +791,12 @@ void SetDetailSize(Service::Interface* self) { } void SetSize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const u32 size = cmd_buff[2] & 0xFF; - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1F, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const u8 size = rp.Pop<u8>(); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -831,49 +806,45 @@ void SetSize(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x1F, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, size=%u, context_select=%u", camera_select.m_val, size, context_select.m_val); } void SetFrameRate(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const FrameRate frame_rate = static_cast<FrameRate>(cmd_buff[2] & 0xFF); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x20, 2, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const FrameRate frame_rate = static_cast<FrameRate>(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid()) { for (int camera : camera_select) { cameras[camera].frame_rate = frame_rate; // TODO(wwylele): consider hinting the actual camera with the expected frame rate } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u", camera_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x20, 1, 0); - LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select=%u, frame_rate=%d", camera_select.m_val, static_cast<int>(frame_rate)); } void SetEffect(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const Effect effect = static_cast<Effect>(cmd_buff[2] & 0xFF); - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x22, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const Effect effect = static_cast<Effect>(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -883,26 +854,24 @@ void SetEffect(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x22, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, effect=%d, context_select=%u", camera_select.m_val, static_cast<int>(effect), context_select.m_val); } void SetOutputFormat(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - const CameraSet camera_select(cmd_buff[1]); - const OutputFormat format = static_cast<OutputFormat>(cmd_buff[2] & 0xFF); - const ContextSet context_select(cmd_buff[3]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x25, 3, 0); + const CameraSet camera_select(rp.Pop<u8>()); + const OutputFormat format = static_cast<OutputFormat>(rp.Pop<u8>()); + const ContextSet context_select(rp.Pop<u8>()); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera : camera_select) { for (int context : context_select) { @@ -912,34 +881,32 @@ void SetOutputFormat(Service::Interface* self) { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", camera_select.m_val, context_select.m_val); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + rb.Push(ERROR_INVALID_ENUM_VALUE); } - cmd_buff[0] = IPC::MakeHeader(0x25, 1, 0); - LOG_DEBUG(Service_CAM, "called, camera_select=%u, format=%d, context_select=%u", camera_select.m_val, static_cast<int>(format), context_select.m_val); } void SynchronizeVsyncTiming(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x29, 2, 0); + const u8 camera_select1 = rp.Pop<u8>(); + const u8 camera_select2 = rp.Pop<u8>(); - const u32 camera_select1 = cmd_buff[1] & 0xFF; - const u32 camera_select2 = cmd_buff[2] & 0xFF; - - cmd_buff[0] = IPC::MakeHeader(0x29, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called, camera_select1=%u, camera_select2=%u", camera_select1, camera_select2); } void GetStereoCameraCalibrationData(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestBuilder rb = + IPC::RequestParser(Kernel::GetCommandBuffer(), 0x2B, 0, 0).MakeBuilder(17, 0); // Default values taken from yuriks' 3DS. Valid data is required here or games using the // calibration get stuck in an infinite CPU loop. @@ -958,34 +925,28 @@ void GetStereoCameraCalibrationData(Service::Interface* self) { data.imageWidth = 640; data.imageHeight = 480; - cmd_buff[0] = IPC::MakeHeader(0x2B, 17, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - memcpy(&cmd_buff[2], &data, sizeof(data)); + rb.Push(RESULT_SUCCESS); + rb.PushRaw(data); LOG_TRACE(Service_CAM, "called"); } void SetPackageParameterWithoutContext(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x33, 11, 0); PackageParameterWithoutContext package; - std::memcpy(&package, cmd_buff + 1, sizeof(package)); + rp.PopRaw(package); - cmd_buff[0] = IPC::MakeHeader(0x33, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called"); } -template <typename PackageParameterType, int command_id> -static void SetPackageParameter() { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - PackageParameterType package; - std::memcpy(&package, cmd_buff + 1, sizeof(package)); - - const CameraSet camera_select(static_cast<u32>(package.camera_select)); - const ContextSet context_select(static_cast<u32>(package.context_select)); +template <typename PackageParameterType> +static ResultCode SetPackageParameter(const PackageParameterType& package) { + const CameraSet camera_select(package.camera_select); + const ContextSet context_select(package.context_select); if (camera_select.IsValid() && context_select.IsValid()) { for (int camera_id : camera_select) { @@ -1002,53 +963,66 @@ static void SetPackageParameter() { } } } - cmd_buff[1] = RESULT_SUCCESS.raw; + return RESULT_SUCCESS; } else { LOG_ERROR(Service_CAM, "invalid camera_select=%u, context_select=%u", package.camera_select, package.context_select); - cmd_buff[1] = ERROR_INVALID_ENUM_VALUE.raw; + return ERROR_INVALID_ENUM_VALUE; } - - cmd_buff[0] = IPC::MakeHeader(command_id, 1, 0); - - LOG_DEBUG(Service_CAM, "called"); } -Resolution PackageParameterWithContext::GetResolution() { +Resolution PackageParameterWithContext::GetResolution() const { return PRESET_RESOLUTION[static_cast<int>(size)]; } void SetPackageParameterWithContext(Service::Interface* self) { - SetPackageParameter<PackageParameterWithContext, 0x34>(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x34, 5, 0); + + PackageParameterWithContext package; + rp.PopRaw(package); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + ResultCode result = SetPackageParameter(package); + rb.Push(result); + + LOG_DEBUG(Service_CAM, "called"); } void SetPackageParameterWithContextDetail(Service::Interface* self) { - SetPackageParameter<PackageParameterWithContextDetail, 0x35>(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x35, 7, 0); + + PackageParameterWithContextDetail package; + rp.PopRaw(package); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + ResultCode result = SetPackageParameter(package); + rb.Push(result); + + LOG_DEBUG(Service_CAM, "called"); } void GetSuitableY2rStandardCoefficient(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[0] = IPC::MakeHeader(0x36, 2, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x36, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); LOG_WARNING(Service_CAM, "(STUBBED) called"); } void PlayShutterSound(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u8 sound_id = cmd_buff[1] & 0xFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x38, 1, 0); + u8 sound_id = rp.Pop<u8>(); - cmd_buff[0] = IPC::MakeHeader(0x38, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_WARNING(Service_CAM, "(STUBBED) called, sound_id=%d", sound_id); } void DriverInitialize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x39, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); for (int camera_id = 0; camera_id < NumCameras; ++camera_id) { CameraConfig& camera = cameras[camera_id]; @@ -1074,14 +1048,14 @@ void DriverInitialize(Service::Interface* self) { port.Clear(); } - cmd_buff[0] = IPC::MakeHeader(0x39, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_CAM, "called"); } void DriverFinalize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3A, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); CancelReceiving(0); CancelReceiving(1); @@ -1090,8 +1064,7 @@ void DriverFinalize(Service::Interface* self) { camera.impl = nullptr; } - cmd_buff[0] = IPC::MakeHeader(0x3A, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_CAM, "called"); } diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index 34a9c8479..b6da721d8 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -184,9 +184,10 @@ struct PackageParameterWithoutContext { s16 auto_white_balance_window_y; s16 auto_white_balance_window_width; s16 auto_white_balance_window_height; + INSERT_PADDING_WORDS(4); }; -static_assert(sizeof(PackageParameterWithoutContext) == 28, +static_assert(sizeof(PackageParameterWithoutContext) == 44, "PackageParameterCameraWithoutContext structure size is wrong"); struct PackageParameterWithContext { @@ -196,11 +197,12 @@ struct PackageParameterWithContext { Effect effect; Size size; INSERT_PADDING_BYTES(3); + INSERT_PADDING_WORDS(3); - Resolution GetResolution(); + Resolution GetResolution() const; }; -static_assert(sizeof(PackageParameterWithContext) == 8, +static_assert(sizeof(PackageParameterWithContext) == 20, "PackageParameterWithContext structure size is wrong"); struct PackageParameterWithContextDetail { @@ -209,13 +211,14 @@ struct PackageParameterWithContextDetail { Flip flip; Effect effect; Resolution resolution; + INSERT_PADDING_WORDS(3); - Resolution GetResolution() { + Resolution GetResolution() const { return resolution; } }; -static_assert(sizeof(PackageParameterWithContextDetail) == 16, +static_assert(sizeof(PackageParameterWithContextDetail) == 28, "PackageParameterWithContextDetail structure size is wrong"); /** diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index eb04273db..bd9814244 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -3,7 +3,10 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/result.h" #include "core/hle/service/cecd/cecd.h" #include "core/hle/service/cecd/cecd_ndm.h" #include "core/hle/service/cecd/cecd_s.h" diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index caa41ded7..5a7878b31 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -13,6 +13,8 @@ #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/result.h" #include "core/hle/service/cfg/cfg.h" #include "core/hle/service/cfg/cfg_i.h" @@ -21,6 +23,7 @@ #include "core/hle/service/cfg/cfg_u.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/service.h" +#include "core/memory.h" #include "core/settings.h" namespace Service { diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 6cf62f9bc..1455f20ca 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -4,9 +4,12 @@ #include <cstring> #include "common/alignment.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/csnd_snd.h" +#include "core/memory.h" namespace Service { namespace CSND { diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 25c07f401..32cfa2c44 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -4,6 +4,7 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/result.h" #include "core/hle/service/dlp/dlp_srvr.h" diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 79171a0bc..363066d14 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -3,12 +3,18 @@ // Refer to the license.txt file included. #include <algorithm> +#include <array> #include <cinttypes> #include "audio_core/hle/pipe.h" +#include "common/assert.h" #include "common/hash.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/result.h" #include "core/hle/service/dsp_dsp.h" +#include "core/memory.h" using DspPipe = DSP::HLE::DspPipe; diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 9da55f328..7c8f4339f 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -6,10 +6,11 @@ #include <chrono> #include <iomanip> #include <sstream> - #include "common/bit_field.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core.h" +#include "core/hle/ipc.h" #include "core/hle/result.h" #include "core/hle/service/err_f.h" @@ -172,6 +173,7 @@ static void ThrowFatalError(Interface* self) { const ErrInfo* errinfo = reinterpret_cast<ErrInfo*>(&cmd_buff[1]); LOG_CRITICAL(Service_ERR, "Fatal error type: %s", GetErrType(errinfo->errinfo_common.specifier).c_str()); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorUnknown); // Generic Info LogGenericInfo(errinfo->errinfo_common); diff --git a/src/core/hle/service/frd/frd.cpp b/src/core/hle/service/frd/frd.cpp index 34fdf7f53..76ecda8b7 100644 --- a/src/core/hle/service/frd/frd.cpp +++ b/src/core/hle/service/frd/frd.cpp @@ -2,11 +2,16 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "common/assert.h" +#include "common/logging/log.h" #include "common/string_util.h" +#include "core/hle/ipc.h" +#include "core/hle/result.h" #include "core/hle/service/frd/frd.h" #include "core/hle/service/frd/frd_a.h" #include "core/hle/service/frd/frd_u.h" #include "core/hle/service/service.h" +#include "core/memory.h" namespace Service { namespace FRD { diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 632712f2c..3605ef175 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -24,7 +24,11 @@ #include "core/file_sys/directory_backend.h" #include "core/file_sys/errors.h" #include "core/file_sys/file_backend.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -83,6 +87,10 @@ File::File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& File::~File() {} void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) { + using Kernel::ClientSession; + using Kernel::ServerSession; + using Kernel::SharedPtr; + u32* cmd_buff = Kernel::GetCommandBuffer(); FileCommand cmd = static_cast<FileCommand>(cmd_buff[0]); switch (cmd) { @@ -161,10 +169,9 @@ void File::HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_ses case FileCommand::OpenLinkFile: { LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); - auto sessions = Kernel::ServerSession::CreateSessionPair(GetName(), shared_from_this()); - ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + auto sessions = ServerSession::CreateSessionPair(GetName()); + ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + cmd_buff[3] = Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)) .ValueOr(INVALID_HANDLE); break; } @@ -258,9 +265,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi auto itr = id_code_map.find(id_code); if (itr == id_code_map.end()) { - // TODO: Verify error against hardware - return ResultCode(ErrorDescription::NotFound, ErrorModule::FS, ErrorSummary::NotFound, - ErrorLevel::Permanent); + return FileSys::ERROR_NOT_FOUND; } CASCADE_RESULT(std::unique_ptr<ArchiveBackend> res, itr->second->Open(archive_path)); diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 2ea956e0b..3a3371c88 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -8,7 +8,7 @@ #include <string> #include "common/common_types.h" #include "core/file_sys/archive_backend.h" -#include "core/hle/kernel/server_session.h" +#include "core/hle/kernel/hle_ipc.h" #include "core/hle/result.h" namespace FileSys { @@ -43,7 +43,7 @@ enum class MediaType : u32 { NAND = 0, SDMC = 1, GameCard = 2 }; typedef u64 ArchiveHandle; -class File final : public SessionRequestHandler, public std::enable_shared_from_this<File> { +class File final : public Kernel::SessionRequestHandler { public: File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path); ~File(); @@ -60,7 +60,7 @@ protected: void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) override; }; -class Directory final : public SessionRequestHandler { +class Directory final : public Kernel::SessionRequestHandler { public: Directory(std::unique_ptr<FileSys::DirectoryBackend>&& backend, const FileSys::Path& path); ~Directory(); diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index e53a970d3..34e1783ec 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -8,8 +8,13 @@ #include "common/logging/log.h" #include "common/scope_exit.h" #include "common/string_util.h" +#include "core/core.h" #include "core/file_sys/errors.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/fs/fs_user.h" @@ -18,8 +23,9 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace FS_User -using Kernel::SharedPtr; +using Kernel::ClientSession; using Kernel::ServerSession; +using Kernel::SharedPtr; namespace Service { namespace FS { @@ -77,11 +83,11 @@ static void OpenFile(Service::Interface* self) { rb.Push(file_res.Code()); if (file_res.Succeeded()) { std::shared_ptr<File> file = *file_res; - auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); - file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - rb.PushMoveHandles(Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom()); + auto sessions = ServerSession::CreateSessionPair(file->GetName()); + file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + + rb.PushMoveHandles( + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom()); } else { rb.PushMoveHandles(0); LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); @@ -130,7 +136,7 @@ static void OpenFileDirectly(Service::Interface* self) { ResultVal<ArchiveHandle> archive_handle = OpenArchive(archive_id, archive_path); if (archive_handle.Failed()) { LOG_ERROR(Service_FS, - "failed to get a handle for archive archive_id=0x%08X archive_path=%s", + "Failed to get a handle for archive archive_id=0x%08X archive_path=%s", static_cast<u32>(archive_id), archive_path.DebugStr().c_str()); cmd_buff[1] = archive_handle.Code().raw; cmd_buff[3] = 0; @@ -143,11 +149,11 @@ static void OpenFileDirectly(Service::Interface* self) { cmd_buff[1] = file_res.Code().raw; if (file_res.Succeeded()) { std::shared_ptr<File> file = *file_res; - auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); - file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom(); + auto sessions = ServerSession::CreateSessionPair(file->GetName()); + file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + + cmd_buff[3] = + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", @@ -410,11 +416,11 @@ static void OpenDirectory(Service::Interface* self) { cmd_buff[1] = dir_res.Code().raw; if (dir_res.Succeeded()) { std::shared_ptr<Directory> directory = *dir_res; - auto sessions = ServerSession::CreateSessionPair(directory->GetName(), directory); - directory->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom(); + auto sessions = ServerSession::CreateSessionPair(directory->GetName()); + directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); + + cmd_buff[3] = + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); } else { LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 94f6b8a9c..6ff0f4812 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -5,7 +5,9 @@ #include "common/bit_field.h" #include "common/microprofile.h" #include "core/core.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" #include "core/hle/service/gsp_gpu.h" diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 64d01cdd7..5255f6dc8 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -10,7 +10,9 @@ #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" #include "core/hle/service/hid/hid_spvr.h" diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 53807cd91..0de698003 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -6,6 +6,7 @@ #include "common/bit_field.h" #include "core/core_timing.h" #include "core/frontend/input.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index 369115f09..fdecdce64 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -7,6 +7,7 @@ #include <boost/optional.hpp> #include "common/string_util.h" #include "common/swap.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/extra_hid.h" diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index d1e6d869f..7255ea026 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -7,6 +7,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/ldr_ro/cro_helper.h" diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index e98388560..35212b59b 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -3,7 +3,9 @@ // Refer to the license.txt file included. #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/mic_u.h" diff --git a/src/core/hle/service/ndm/ndm.cpp b/src/core/hle/service/ndm/ndm.cpp index 5eb97f0d3..096c0cdac 100644 --- a/src/core/hle/service/ndm/ndm.cpp +++ b/src/core/hle/service/ndm/ndm.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/service/ndm/ndm.h" #include "core/hle/service/ndm/ndm_u.h" #include "core/hle/service/service.h" diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index fd3c7d9c2..b44a9f668 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -2,7 +2,9 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/ipc.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nfc/nfc_m.h" #include "core/hle/service/nfc/nfc_u.h" diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 63c334cb2..d5624fe54 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -4,6 +4,7 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_aoc.h" #include "core/hle/service/nim/nim_s.h" diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index a82cc9bd3..6c4600f25 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -9,6 +9,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/core_timing.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp index c6e5bc5f1..6332b404c 100644 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -3,14 +3,13 @@ // Refer to the license.txt file included. #include <cstring> - -#include "core/hle/service/nwm/nwm_uds.h" -#include "core/hle/service/nwm/uds_beacon.h" - #include <cryptopp/aes.h> #include <cryptopp/md5.h> #include <cryptopp/modes.h> #include <cryptopp/sha.h> +#include "common/assert.h" +#include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_beacon.h" namespace Service { namespace NWM { diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 0672ac2e3..0d443aa44 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -2,11 +2,11 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <boost/range/algorithm_ext/erase.hpp> - #include "common/logging/log.h" #include "common/string_util.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/service/ac/ac.h" #include "core/hle/service/act/act.h" #include "core/hle/service/am/am.h" @@ -39,15 +39,15 @@ #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/qtm/qtm.h" #include "core/hle/service/service.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/srv.h" #include "core/hle/service/soc_u.h" -#include "core/hle/service/srv.h" #include "core/hle/service/ssl_c.h" #include "core/hle/service/y2r_u.h" namespace Service { std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; -std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; /** * Creates a function string for logging, complete with the name (or header code, depending @@ -66,16 +66,6 @@ static std::string MakeFunctionString(const char* name, const char* port_name, return function_string; } -void SessionRequestHandler::ClientConnected( - Kernel::SharedPtr<Kernel::ServerSession> server_session) { - connected_sessions.push_back(server_session); -} - -void SessionRequestHandler::ClientDisconnected( - Kernel::SharedPtr<Kernel::ServerSession> server_session) { - boost::range::remove_erase(connected_sessions, server_session); -} - Interface::Interface(u32 max_sessions) : max_sessions(max_sessions) {} Interface::~Interface() = default; @@ -116,24 +106,27 @@ void Interface::Register(const FunctionInfo* functions, size_t n) { // Module interface static void AddNamedPort(Interface* interface_) { - auto ports = - Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), - std::shared_ptr<Interface>(interface_)); - auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); + Kernel::SharedPtr<Kernel::ServerPort> server_port; + Kernel::SharedPtr<Kernel::ClientPort> client_port; + std::tie(server_port, client_port) = + Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName()); + + server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); g_kernel_named_ports.emplace(interface_->GetPortName(), std::move(client_port)); } void AddService(Interface* interface_) { - auto ports = - Kernel::ServerPort::CreatePortPair(interface_->GetMaxSessions(), interface_->GetPortName(), - std::shared_ptr<Interface>(interface_)); - auto client_port = std::get<Kernel::SharedPtr<Kernel::ClientPort>>(ports); - g_srv_services.emplace(interface_->GetPortName(), std::move(client_port)); + auto server_port = + SM::g_service_manager + ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) + .MoveFrom(); + server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); } /// Initialize ServiceManager void Init() { - AddNamedPort(new SRV::SRV); + SM::g_service_manager = std::make_unique<SM::ServiceManager>(); + AddNamedPort(new SM::SRV); AddNamedPort(new ERR::ERR_F); FS::ArchiveInit(); @@ -194,7 +187,7 @@ void Shutdown() { AC::Shutdown(); FS::ArchiveShutdown(); - g_srv_services.clear(); + SM::g_service_manager = nullptr; g_kernel_named_ports.clear(); LOG_DEBUG(Service, "shutdown OK"); } diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index e6a5f1417..8933d57cc 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -8,21 +8,19 @@ #include <string> #include <unordered_map> #include <boost/container/flat_map.hpp> +#include "common/bit_field.h" #include "common/common_types.h" -#include "core/hle/ipc.h" -#include "core/hle/ipc_helpers.h" -#include "core/hle/kernel/client_port.h" -#include "core/hle/kernel/thread.h" -#include "core/hle/result.h" -#include "core/memory.h" +#include "core/hle/kernel/hle_ipc.h" +#include "core/hle/kernel/kernel.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace Service namespace Kernel { +class ClientPort; class ServerSession; } -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace Service - namespace Service { static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 characters) @@ -30,48 +28,10 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character static const u32 DefaultMaxSessions = 10; /** - * Interface implemented by HLE Session handlers. - * This can be provided to a ServerSession in order to hook into several relevant events - * (such as a new connection or a SyncRequest) so they can be implemented in the emulator. - */ -class SessionRequestHandler { -public: - /** - * Handles a sync request from the emulated application. - * @param server_session The ServerSession that was triggered for this sync request, - * it should be used to differentiate which client (As in ClientSession) we're answering to. - * TODO(Subv): Use a wrapper structure to hold all the information relevant to - * this request (ServerSession, Originator thread, Translated command buffer, etc). - * @returns ResultCode the result code of the translate operation. - */ - virtual void HandleSyncRequest(Kernel::SharedPtr<Kernel::ServerSession> server_session) = 0; - - /** - * Signals that a client has just connected to this HLE handler and keeps the - * associated ServerSession alive for the duration of the connection. - * @param server_session Owning pointer to the ServerSession associated with the connection. - */ - void ClientConnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); - - /** - * Signals that a client has just disconnected from this HLE handler and releases the - * associated ServerSession. - * @param server_session ServerSession associated with the connection. - */ - void ClientDisconnected(Kernel::SharedPtr<Kernel::ServerSession> server_session); - -protected: - /// List of sessions that are connected to this handler. - /// A ServerSession whose server endpoint is an HLE implementation is kept alive by this list - // for the duration of the connection. - std::vector<Kernel::SharedPtr<Kernel::ServerSession>> connected_sessions; -}; - -/** * Framework for implementing HLE service handlers which dispatch incoming SyncRequests based on a * table mapping header ids to handler functions. */ -class Interface : public SessionRequestHandler { +class Interface : public Kernel::SessionRequestHandler { public: /** * Creates an HLE interface with the specified max sessions. @@ -149,8 +109,6 @@ void Shutdown(); /// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_kernel_named_ports; -/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. -extern std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> g_srv_services; /// Adds a service to the services table void AddService(Interface* interface_); diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp new file mode 100644 index 000000000..361f7a0a9 --- /dev/null +++ b/src/core/hle/service/sm/sm.cpp @@ -0,0 +1,59 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <tuple> +#include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/server_port.h" +#include "core/hle/result.h" +#include "core/hle/service/sm/sm.h" + +namespace Service { +namespace SM { + +static ResultCode ValidateServiceName(const std::string& name) { + if (name.size() <= 0 || name.size() > 8) { + return ERR_INVALID_NAME_SIZE; + } + if (name.find('\0') != std::string::npos) { + return ERR_NAME_CONTAINS_NUL; + } + return RESULT_SUCCESS; +} + +ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService( + std::string name, unsigned int max_sessions) { + + CASCADE_CODE(ValidateServiceName(name)); + Kernel::SharedPtr<Kernel::ServerPort> server_port; + Kernel::SharedPtr<Kernel::ClientPort> client_port; + std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); + + registered_services.emplace(name, std::move(client_port)); + return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)); +} + +ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort( + const std::string& name) { + + CASCADE_CODE(ValidateServiceName(name)); + auto it = registered_services.find(name); + if (it == registered_services.end()) { + return ERR_SERVICE_NOT_REGISTERED; + } + + return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second); +} + +ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService( + const std::string& name) { + + CASCADE_RESULT(auto client_port, GetServicePort(name)); + return client_port->Connect(); +} + +std::unique_ptr<ServiceManager> g_service_manager; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h new file mode 100644 index 000000000..5fac5455c --- /dev/null +++ b/src/core/hle/service/sm/sm.h @@ -0,0 +1,49 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <string> +#include <unordered_map> +#include "core/hle/kernel/kernel.h" +#include "core/hle/result.h" +#include "core/hle/service/service.h" + +namespace Kernel { +class ClientPort; +class ClientSession; +class ServerPort; +class SessionRequestHandler; +} // namespace Kernel + +namespace Service { +namespace SM { + +constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(1, ErrorModule::SRV, ErrorSummary::WouldBlock, + ErrorLevel::Temporary); // 0xD0406401 +constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(2, ErrorModule::SRV, ErrorSummary::WouldBlock, + ErrorLevel::Temporary); // 0xD0406402 +constexpr ResultCode ERR_INVALID_NAME_SIZE(5, ErrorModule::SRV, ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9006405 +constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::InvalidArgument, + ErrorLevel::Permanent); // 0xD8E06406 +constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9006407 + +class ServiceManager { +public: + ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name, + unsigned int max_sessions); + ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name); + ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name); + +private: + /// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. + std::unordered_map<std::string, Kernel::SharedPtr<Kernel::ClientPort>> registered_services; +}; + +extern std::unique_ptr<ServiceManager> g_service_manager; + +} // namespace SM +} // namespace Service diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/sm/srv.cpp index 130c9d25e..063b1b0fc 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -6,15 +6,21 @@ #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" -#include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" +#include "core/hle/kernel/semaphore.h" #include "core/hle/kernel/server_session.h" -#include "core/hle/service/srv.h" +#include "core/hle/service/sm/sm.h" +#include "core/hle/service/sm/srv.h" namespace Service { -namespace SRV { +namespace SM { -static Kernel::SharedPtr<Kernel::Event> event_handle; +constexpr int MAX_PENDING_NOTIFICATIONS = 16; + +static Kernel::SharedPtr<Kernel::Semaphore> notification_semaphore; /** * SRV::RegisterClient service function @@ -51,14 +57,13 @@ static void RegisterClient(Interface* self) { static void EnableNotification(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - // TODO(bunnei): Change to a semaphore once these have been implemented - event_handle = Kernel::Event::Create(Kernel::ResetType::OneShot, "SRV:Event"); - event_handle->Clear(); + notification_semaphore = + Kernel::Semaphore::Create(0, MAX_PENDING_NOTIFICATIONS, "SRV:Notification").Unwrap(); cmd_buff[0] = IPC::MakeHeader(0x2, 0x1, 0x2); // 0x20042 cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = IPC::CopyHandleDesc(1); - cmd_buff[3] = Kernel::g_handle_table.Create(event_handle).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(notification_semaphore).MoveFrom(); LOG_WARNING(Service_SRV, "(STUBBED) called"); } @@ -77,25 +82,41 @@ static void GetServiceHandle(Interface* self) { ResultCode res = RESULT_SUCCESS; u32* cmd_buff = Kernel::GetCommandBuffer(); - std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); - auto it = Service::g_srv_services.find(port_name); + size_t name_len = cmd_buff[3]; + if (name_len > Service::kMaxPortSize) { + cmd_buff[1] = ERR_INVALID_NAME_SIZE.raw; + LOG_ERROR(Service_SRV, "called name_len=0x%X, failed with code=0x%08X", name_len, + cmd_buff[1]); + return; + } + std::string name(reinterpret_cast<const char*>(&cmd_buff[1]), name_len); + bool return_port_on_failure = (cmd_buff[4] & 1) == 0; - if (it != Service::g_srv_services.end()) { - auto client_port = it->second; + // TODO(yuriks): Permission checks go here - auto client_session = client_port->Connect(); - res = client_session.Code(); + auto client_port = g_service_manager->GetServicePort(name); + if (client_port.Failed()) { + cmd_buff[1] = client_port.Code().raw; + LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), + cmd_buff[1]); + return; + } - if (client_session.Succeeded()) { - // Return the client session - cmd_buff[3] = Kernel::g_handle_table.Create(*client_session).MoveFrom(); - } - LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); + auto session = client_port.Unwrap()->Connect(); + cmd_buff[1] = session.Code().raw; + if (session.Succeeded()) { + cmd_buff[3] = Kernel::g_handle_table.Create(session.MoveFrom()).MoveFrom(); + LOG_DEBUG(Service_SRV, "called service=%s, session handle=0x%08X", name.c_str(), + cmd_buff[3]); + } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { + cmd_buff[1] = ERR_MAX_CONNECTIONS_REACHED.raw; + cmd_buff[3] = Kernel::g_handle_table.Create(client_port.MoveFrom()).MoveFrom(); + LOG_WARNING(Service_SRV, "called service=%s, *port* handle=0x%08X", name.c_str(), + cmd_buff[3]); } else { - LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); - res = UnimplementedFunction(ErrorModule::SRV); + LOG_ERROR(Service_SRV, "called service=%s, failed with code=0x%08X", name.c_str(), + cmd_buff[1]); } - cmd_buff[1] = res.raw; } /** @@ -177,12 +198,12 @@ const Interface::FunctionInfo FunctionTable[] = { SRV::SRV() { Register(FunctionTable); - event_handle = nullptr; + notification_semaphore = nullptr; } SRV::~SRV() { - event_handle = nullptr; + notification_semaphore = nullptr; } -} // namespace SRV +} // namespace SM } // namespace Service diff --git a/src/core/hle/service/srv.h b/src/core/hle/service/sm/srv.h index d3a9de879..4196ca1e2 100644 --- a/src/core/hle/service/srv.h +++ b/src/core/hle/service/sm/srv.h @@ -4,10 +4,11 @@ #pragma once +#include <string> #include "core/hle/service/service.h" namespace Service { -namespace SRV { +namespace SM { /// Interface to "srv:" service class SRV final : public Interface { @@ -20,5 +21,5 @@ public: } }; -} // namespace SRV +} // namespace SM } // namespace Service diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index 530614e6f..3d215d42d 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -11,6 +11,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/scope_exit.h" +#include "core/hle/ipc.h" #include "core/hle/kernel/server_session.h" #include "core/hle/result.h" #include "core/hle/service/soc_u.h" diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index 09ced9d7a..300acca75 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp @@ -4,7 +4,9 @@ #include <random> #include "common/common_types.h" +#include "core/hle/ipc.h" #include "core/hle/service/ssl_c.h" +#include "core/memory.h" namespace Service { namespace SSL { diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index c0837d49d..bb7bf2d67 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -6,6 +6,8 @@ #include "common/common_funcs.h" #include "common/common_types.h" #include "common/logging/log.h" +#include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/kernel.h" #include "core/hle/service/y2r_u.h" diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 30230d65a..e68b9f16a 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> #include <cinttypes> #include <map> #include "common/logging/log.h" @@ -16,6 +17,7 @@ #include "core/hle/kernel/client_session.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/process.h" @@ -27,6 +29,7 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/timer.h" #include "core/hle/kernel/vm_manager.h" +#include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -244,7 +247,7 @@ static ResultCode CloseHandle(Kernel::Handle handle) { /// Wait for a handle to synchronize, timeout after the specified nanoseconds static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) { - auto object = Kernel::g_handle_table.GetWaitObject(handle); + auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handle); Kernel::Thread* thread = Kernel::GetCurrentThread(); if (object == nullptr) @@ -299,7 +302,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha std::vector<ObjectPtr> objects(handle_count); for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + auto object = Kernel::g_handle_table.Get<Kernel::WaitObject>(handles[i]); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 1d80766ae..48bbf687d 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -8,6 +8,7 @@ #include <initializer_list> #include <memory> #include <string> +#include <utility> #include <vector> #include <boost/optional.hpp> #include "common/common_types.h" @@ -100,11 +101,11 @@ public: * Loads the system mode that this application needs. * This function defaults to 2 (96MB allocated to the application) if it can't read the * information. - * @returns Optional with the kernel system mode + * @returns A pair with the optional system mode, and and the status. */ - virtual boost::optional<u32> LoadKernelSystemMode() { + virtual std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() { // 96MB allocated to the application. - return 2; + return std::make_pair(2, ResultStatus::Success); } /** diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index beeb13ffa..ffc019560 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -121,12 +121,16 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -boost::optional<u32> AppLoader_NCCH::LoadKernelSystemMode() { +std::pair<boost::optional<u32>, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { if (!is_loaded) { - if (LoadExeFS() != ResultStatus::Success) - return boost::none; + ResultStatus res = LoadExeFS(); + if (res != ResultStatus::Success) { + return std::make_pair(boost::none, res); + } } - return exheader_header.arm11_system_local_caps.system_mode.Value(); + // Set the system mode as the one from the exheader. + return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(), + ResultStatus::Success); } ResultStatus AppLoader_NCCH::LoadExec() { diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 4ef95b5c6..0ebd47fd5 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -179,9 +179,9 @@ public: /** * Loads the Exheader and returns the system mode for this application. - * @return Optional with the kernel system mode + * @returns A pair with the optional system mode, and and the status. */ - boost::optional<u32> LoadKernelSystemMode() override; + std::pair<boost::optional<u32>, ResultStatus> LoadKernelSystemMode() override; ResultStatus ReadCode(std::vector<u8>& buffer) override; |