diff options
Diffstat (limited to 'src/core/hle')
92 files changed, 2960 insertions, 1578 deletions
diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index a2f51b41b..0b6b6f518 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -33,114 +33,105 @@ static inline void FuncReturn64(u64 res) { } //////////////////////////////////////////////////////////////////////////////////////////////////// -// Function wrappers that return type s32 +// Function wrappers that return type ResultCode -template<s32 func(u32, u32, u32, u32)> void Wrap() { - FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3))); +template<ResultCode func(u32, u32, u32, u32)> void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3)).raw); } -template<s32 func(u32, u32, u32, u32, u32)> void Wrap() { - FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4))); -} - -template<s32 func(u32*, u32, u32, u32, u32, u32)> void Wrap(){ +template<ResultCode func(u32*, u32, u32, u32, u32, u32)> void Wrap(){ u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)); + u32 retval = func(¶m_1, PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(s32*, u32*, s32, bool, s64)> void Wrap() { +template<ResultCode func(s32*, u32*, s32, bool, s64)> void Wrap() { s32 param_1 = 0; s32 retval = func(¶m_1, (Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), - (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))); + (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))).raw; Core::g_app_core->SetReg(1, (u32)param_1); FuncReturn(retval); } -// TODO(bunnei): Is this correct? Probably not - Last parameter looks wrong for ArbitrateAddress -template<s32 func(u32, u32, u32, u32, s64)> void Wrap() { - FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4)))); +template<ResultCode func(u32, u32, u32, u32, s64)> void Wrap() { + FuncReturn(func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), (((s64)PARAM(5) << 32) | PARAM(4))).raw); } -template<s32 func(u32*)> void Wrap(){ +template<ResultCode func(u32*)> void Wrap(){ u32 param_1 = 0; - u32 retval = func(¶m_1); + u32 retval = func(¶m_1).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(u32, s64)> void Wrap() { - FuncReturn(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2)))); +template<ResultCode func(u32, s64)> void Wrap() { + FuncReturn(func(PARAM(0), (((s64)PARAM(3) << 32) | PARAM(2))).raw); } -template<s32 func(void*, void*, u32)> void Wrap(){ - FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2))); +template<ResultCode func(void*, void*, u32)> void Wrap(){ + FuncReturn(func(Memory::GetPointer(PARAM(0)), Memory::GetPointer(PARAM(1)), PARAM(2)).raw); } -template<s32 func(s32*, u32)> void Wrap(){ +template<ResultCode func(s32*, u32)> void Wrap(){ s32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1)); + u32 retval = func(¶m_1, PARAM(1)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(u32, s32)> void Wrap() { - FuncReturn(func(PARAM(0), (s32)PARAM(1))); +template<ResultCode func(u32, s32)> void Wrap() { + FuncReturn(func(PARAM(0), (s32)PARAM(1)).raw); } -template<s32 func(u32*, u32)> void Wrap(){ +template<ResultCode func(u32*, u32)> void Wrap(){ u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1)); + u32 retval = func(¶m_1, PARAM(1)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(u32)> void Wrap() { - FuncReturn(func(PARAM(0))); -} - -template<s32 func(void*)> void Wrap() { - FuncReturn(func(Memory::GetPointer(PARAM(0)))); +template<ResultCode func(u32)> void Wrap() { + FuncReturn(func(PARAM(0)).raw); } -template<s32 func(s64*, u32, void*, s32)> void Wrap(){ +template<ResultCode func(s64*, u32, void*, s32)> void Wrap(){ FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), Memory::GetPointer(PARAM(2)), - (s32)PARAM(3))); + (s32)PARAM(3)).raw); } -template<s32 func(u32*, const char*)> void Wrap() { +template<ResultCode func(u32*, const char*)> void Wrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, Memory::GetCharPointer(PARAM(1))); + u32 retval = func(¶m_1, Memory::GetCharPointer(PARAM(1))).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(u32*, s32, s32)> void Wrap() { +template<ResultCode func(u32*, s32, s32)> void Wrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1), PARAM(2)); + u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(s32*, u32, s32)> void Wrap() { +template<ResultCode func(s32*, u32, s32)> void Wrap() { s32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1), PARAM(2)); + u32 retval = func(¶m_1, PARAM(1), PARAM(2)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(u32*, u32, u32, u32, u32)> void Wrap() { +template<ResultCode func(u32*, u32, u32, u32, u32)> void Wrap() { u32 param_1 = 0; - u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)); + u32 retval = func(¶m_1, PARAM(1), PARAM(2), PARAM(3), PARAM(4)).raw; Core::g_app_core->SetReg(1, param_1); FuncReturn(retval); } -template<s32 func(u32, s64, s64)> void Wrap() { +template<ResultCode func(u32, s64, s64)> void Wrap() { s64 param1 = ((u64)PARAM(3) << 32) | PARAM(2); s64 param2 = ((u64)PARAM(4) << 32) | PARAM(1); - FuncReturn(func(PARAM(0), param1, param2)); + FuncReturn(func(PARAM(0), param1, param2).raw); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/hle.cpp b/src/core/hle/hle.cpp index 5d77a1458..5a2edeb4a 100644 --- a/src/core/hle/hle.cpp +++ b/src/core/hle/hle.cpp @@ -7,10 +7,12 @@ #include "core/arm/arm_interface.h" #include "core/mem_map.h" #include "core/hle/hle.h" +#include "core/hle/shared_page.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/hid/hid.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -69,13 +71,17 @@ void Init() { Service::Init(); Service::FS::ArchiveInit(); Service::CFG::CFGInit(); + Service::HID::HIDInit(); RegisterAllModules(); + SharedPage::Init(); + LOG_DEBUG(Kernel, "initialized OK"); } void Shutdown() { + Service::HID::HIDShutdown(); Service::CFG::CFGShutdown(); Service::FS::ArchiveShutdown(); Service::Shutdown(); diff --git a/src/core/hle/kernel/address_arbiter.cpp b/src/core/hle/kernel/address_arbiter.cpp index 62e3460e1..42f8ce2d9 100644 --- a/src/core/hle/kernel/address_arbiter.cpp +++ b/src/core/hle/kernel/address_arbiter.cpp @@ -15,53 +15,64 @@ namespace Kernel { -class AddressArbiter : public Object { -public: - std::string GetTypeName() const override { return "Arbiter"; } - std::string GetName() const override { return name; } +AddressArbiter::AddressArbiter() {} +AddressArbiter::~AddressArbiter() {} - static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; - HandleType GetHandleType() const override { return HANDLE_TYPE; } +SharedPtr<AddressArbiter> AddressArbiter::Create(std::string name) { + SharedPtr<AddressArbiter> address_arbiter(new AddressArbiter); - std::string name; ///< Name of address arbiter object (optional) -}; + address_arbiter->name = std::move(name); -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/// Arbitrate an address -ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value) { - Object* object = Kernel::g_handle_table.GetGeneric(handle).get(); - if (object == nullptr) - return InvalidHandle(ErrorModule::Kernel); + return address_arbiter; +} +ResultCode AddressArbiter::ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, + u64 nanoseconds) { switch (type) { // Signal thread(s) waiting for arbitrate address... case ArbitrationType::Signal: // Negative value means resume all threads if (value < 0) { - ArbitrateAllThreads(object, address); + ArbitrateAllThreads(address); } else { // Resume first N threads for(int i = 0; i < value; i++) - ArbitrateHighestPriorityThread(object, address); + ArbitrateHighestPriorityThread(address); } break; // Wait current thread (acquire the arbiter)... case ArbitrationType::WaitIfLessThan: if ((s32)Memory::Read32(address) <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(address); + HLE::Reschedule(__func__); + } + break; + case ArbitrationType::WaitIfLessThanWithTimeout: + if ((s32)Memory::Read32(address) <= value) { + Kernel::WaitCurrentThread_ArbitrateAddress(address); + GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } break; - case ArbitrationType::DecrementAndWaitIfLessThan: { s32 memory_value = Memory::Read32(address) - 1; Memory::Write32(address, memory_value); if (memory_value <= value) { - Kernel::WaitCurrentThread(WAITTYPE_ARB, object, address); + Kernel::WaitCurrentThread_ArbitrateAddress(address); + HLE::Reschedule(__func__); + } + break; + } + case ArbitrationType::DecrementAndWaitIfLessThanWithTimeout: + { + s32 memory_value = Memory::Read32(address) - 1; + Memory::Write32(address, memory_value); + if (memory_value <= value) { + Kernel::WaitCurrentThread_ArbitrateAddress(address); + GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } break; @@ -74,20 +85,4 @@ ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s3 return RESULT_SUCCESS; } -/// Create an address arbiter -AddressArbiter* CreateAddressArbiter(Handle& handle, const std::string& name) { - AddressArbiter* address_arbiter = new AddressArbiter; - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(address_arbiter).ValueOr(INVALID_HANDLE); - address_arbiter->name = name; - return address_arbiter; -} - -/// Create an address arbiter -Handle CreateAddressArbiter(const std::string& name) { - Handle handle; - CreateAddressArbiter(handle, name); - return handle; -} - } // namespace Kernel diff --git a/src/core/hle/kernel/address_arbiter.h b/src/core/hle/kernel/address_arbiter.h index 030e7ad7b..8f6a1a8df 100644 --- a/src/core/hle/kernel/address_arbiter.h +++ b/src/core/hle/kernel/address_arbiter.h @@ -18,7 +18,6 @@ namespace Kernel { -/// Address arbitration types enum class ArbitrationType : u32 { Signal, WaitIfLessThan, @@ -27,10 +26,29 @@ enum class ArbitrationType : u32 { DecrementAndWaitIfLessThanWithTimeout, }; -/// Arbitrate an address -ResultCode ArbitrateAddress(Handle handle, ArbitrationType type, u32 address, s32 value); +class AddressArbiter final : public Object { +public: + /** + * Creates an address arbiter. + * + * @param name Optional name used for debugging. + * @returns The created AddressArbiter. + */ + static SharedPtr<AddressArbiter> Create(std::string name = "Unknown"); -/// Create an address arbiter -Handle CreateAddressArbiter(const std::string& name = "Unknown"); + std::string GetTypeName() const override { return "Arbiter"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::AddressArbiter; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + std::string name; ///< Name of address arbiter object (optional) + + ResultCode ArbitrateAddress(ArbitrationType type, VAddr address, s32 value, u64 nanoseconds); + +private: + AddressArbiter(); + ~AddressArbiter() override; +}; } // namespace FileSys diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/event.cpp index 271190dbe..898e1c98f 100644 --- a/src/core/hle/kernel/event.cpp +++ b/src/core/hle/kernel/event.cpp @@ -14,144 +14,38 @@ namespace Kernel { -class Event : public Object { -public: - std::string GetTypeName() const override { return "Event"; } - std::string GetName() const override { return name; } +Event::Event() {} +Event::~Event() {} - static const HandleType HANDLE_TYPE = HandleType::Event; - HandleType GetHandleType() const override { return HANDLE_TYPE; } +SharedPtr<Event> Event::Create(ResetType reset_type, std::string name) { + SharedPtr<Event> evt(new Event); - ResetType intitial_reset_type; ///< ResetType specified at Event initialization - ResetType reset_type; ///< Current ResetType - - bool locked; ///< Event signal wait - bool permanent_locked; ///< Hack - to set event permanent state (for easy passthrough) - std::vector<Handle> waiting_threads; ///< Threads that are waiting for the event - std::string name; ///< Name of event (optional) - - ResultVal<bool> WaitSynchronization() override { - bool wait = locked; - if (locked) { - Handle thread = GetCurrentThread()->GetHandle(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } - Kernel::WaitCurrentThread(WAITTYPE_EVENT, this); - } - if (reset_type != RESETTYPE_STICKY && !permanent_locked) { - locked = true; - } - return MakeResult<bool>(wait); - } -}; - -/** - * Hackish function to set an events permanent lock state, used to pass through synch blocks - * @param handle Handle to event to change - * @param permanent_locked Boolean permanent locked value to set event - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SetPermanentLock(Handle handle, const bool permanent_locked) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - - evt->permanent_locked = permanent_locked; - return RESULT_SUCCESS; -} - -/** - * Changes whether an event is locked or not - * @param handle Handle to event to change - * @param locked Boolean locked value to set event - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SetEventLocked(const Handle handle, const bool locked) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); + evt->signaled = false; + evt->reset_type = evt->intitial_reset_type = reset_type; + evt->name = std::move(name); - if (!evt->permanent_locked) { - evt->locked = locked; - } - return RESULT_SUCCESS; + return evt; } -/** - * Signals an event - * @param handle Handle to event to signal - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode SignalEvent(const Handle handle) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); - - // Resume threads waiting for event to signal - bool event_caught = false; - for (size_t i = 0; i < evt->waiting_threads.size(); ++i) { - Thread* thread = Kernel::g_handle_table.Get<Thread>(evt->waiting_threads[i]).get(); - if (thread != nullptr) - thread->ResumeFromWait(); - - // If any thread is signalled awake by this event, assume the event was "caught" and reset - // the event. This will result in the next thread waiting on the event to block. Otherwise, - // the event will not be reset, and the next thread to call WaitSynchronization on it will - // not block. Not sure if this is correct behavior, but it seems to work. - event_caught = true; - } - evt->waiting_threads.clear(); - - if (!evt->permanent_locked) { - evt->locked = event_caught; - } - return RESULT_SUCCESS; +bool Event::ShouldWait() { + return !signaled; } -/** - * Clears an event - * @param handle Handle to event to clear - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode ClearEvent(Handle handle) { - Event* evt = g_handle_table.Get<Event>(handle).get(); - if (evt == nullptr) return InvalidHandle(ErrorModule::Kernel); +void Event::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); - if (!evt->permanent_locked) { - evt->locked = true; - } - return RESULT_SUCCESS; + // Release the event if it's not sticky... + if (reset_type != RESETTYPE_STICKY) + signaled = false; } -/** - * Creates an event - * @param handle Reference to handle for the newly created mutex - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Newly created Event object - */ -Event* CreateEvent(Handle& handle, const ResetType reset_type, const std::string& name) { - Event* evt = new Event; - - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(evt).ValueOr(INVALID_HANDLE); - - evt->locked = true; - evt->permanent_locked = false; - evt->reset_type = evt->intitial_reset_type = reset_type; - evt->name = name; - - return evt; +void Event::Signal() { + signaled = true; + WakeupAllWaitingThreads(); } -/** - * Creates an event - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Handle to newly created Event object - */ -Handle CreateEvent(const ResetType reset_type, const std::string& name) { - Handle handle; - Event* evt = CreateEvent(handle, reset_type, name); - return handle; +void Event::Clear() { + signaled = false; } } // namespace diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/event.h index da793df1a..fba960d2a 100644 --- a/src/core/hle/kernel/event.h +++ b/src/core/hle/kernel/event.h @@ -11,38 +11,36 @@ namespace Kernel { -/** - * Changes whether an event is locked or not - * @param handle Handle to event to change - * @param locked Boolean locked value to set event - */ -ResultCode SetEventLocked(const Handle handle, const bool locked); - -/** - * Hackish function to set an events permanent lock state, used to pass through synch blocks - * @param handle Handle to event to change - * @param permanent_locked Boolean permanent locked value to set event - */ -ResultCode SetPermanentLock(Handle handle, const bool permanent_locked); - -/** - * Signals an event - * @param handle Handle to event to signal - */ -ResultCode SignalEvent(const Handle handle); - -/** - * Clears an event - * @param handle Handle to event to clear - */ -ResultCode ClearEvent(Handle handle); - -/** - * Creates an event - * @param reset_type ResetType describing how to create event - * @param name Optional name of event - * @return Handle to newly created Event object - */ -Handle CreateEvent(const ResetType reset_type, const std::string& name="Unknown"); +class Event final : public WaitObject { +public: + /** + * Creates an event + * @param reset_type ResetType describing how to create event + * @param name Optional name of event + */ + static SharedPtr<Event> Create(ResetType reset_type, std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Event"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Event; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + ResetType intitial_reset_type; ///< ResetType specified at Event initialization + ResetType reset_type; ///< Current ResetType + + bool signaled; ///< Whether the event has already been signaled + std::string name; ///< Name of event (optional) + + bool ShouldWait() override; + void Acquire() override; + + void Signal(); + void Clear(); + +private: + Event(); + ~Event() override; +}; } // namespace diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index d3684896f..52dca4dd8 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -14,10 +14,47 @@ namespace Kernel { +unsigned int Object::next_object_id = 0; + SharedPtr<Thread> g_main_thread = nullptr; HandleTable g_handle_table; u64 g_program_id = 0; +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 (itr != waiting_threads.end()) + waiting_threads.erase(itr); +} + +SharedPtr<Thread> WaitObject::WakeupNextThread() { + if (waiting_threads.empty()) + return nullptr; + + auto next_thread = std::move(waiting_threads.front()); + waiting_threads.erase(waiting_threads.begin()); + + next_thread->ReleaseWaitObject(this); + + return next_thread; +} + +void WaitObject::WakeupAllWaitingThreads() { + auto waiting_threads_copy = waiting_threads; + + // We use a copy because ReleaseWaitObject will remove the thread from this object's + // waiting_threads list + for (auto thread : waiting_threads_copy) + thread->ReleaseWaitObject(this); + + _assert_msg_(Kernel, waiting_threads.empty(), "failed to awaken all waiting threads!"); +} + HandleTable::HandleTable() { next_generation = 1; Clear(); @@ -39,13 +76,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) { // CTR-OS doesn't use generation 0, so skip straight to 1. if (next_generation >= (1 << 15)) next_generation = 1; - Handle handle = generation | (slot << 15); - if (obj->handle == INVALID_HANDLE) - obj->handle = handle; - generations[slot] = generation; objects[slot] = std::move(obj); + Handle handle = generation | (slot << 15); return MakeResult<Handle>(handle); } @@ -63,11 +97,10 @@ ResultCode HandleTable::Close(Handle handle) { return ERR_INVALID_HANDLE; size_t slot = GetSlot(handle); - u16 generation = GetGeneration(handle); objects[slot] = nullptr; - generations[generation] = next_free_slot; + generations[slot] = next_free_slot; next_free_slot = slot; return RESULT_SUCCESS; } diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 5e5217b78..4d8e388b6 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -8,12 +8,19 @@ #include <array> #include <string> +#include <vector> + #include "common/common.h" #include "core/hle/result.h" typedef u32 Handle; typedef s32 Result; +// TODO: It would be nice to eventually replace these with strong types that prevent accidental +// conversion between each other. +typedef u32 VAddr; ///< Represents a pointer in the userspace virtual address space. +typedef u32 PAddr; ///< Represents a pointer in the ARM11 physical address space. + const Handle INVALID_HANDLE = 0; namespace Kernel { @@ -24,7 +31,8 @@ class Thread; const ResultCode ERR_OUT_OF_HANDLES(ErrorDescription::OutOfMemory, ErrorModule::Kernel, ErrorSummary::OutOfResource, ErrorLevel::Temporary); // TOOD: Verify code -const ResultCode ERR_INVALID_HANDLE = InvalidHandle(ErrorModule::Kernel); +const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); enum KernelHandle : Handle { CurrentThread = 0xFFFF8000, @@ -50,32 +58,51 @@ enum { DEFAULT_STACK_SIZE = 0x4000, }; -class HandleTable; - class Object : NonCopyable { - friend class HandleTable; - u32 handle = INVALID_HANDLE; public: virtual ~Object() {} - Handle GetHandle() const { return handle; } + + /// Returns a unique identifier for the object. For debugging purposes only. + unsigned int GetObjectId() const { return object_id; } + virtual std::string GetTypeName() const { return "[BAD KERNEL OBJECT TYPE]"; } virtual std::string GetName() const { return "[UNKNOWN KERNEL OBJECT]"; } virtual Kernel::HandleType GetHandleType() const = 0; /** - * Wait for kernel object to synchronize. - * @return True if the current thread should wait as a result of the wait + * Check if a thread can wait on the object + * @return True if a thread can wait on the object, otherwise false */ - virtual ResultVal<bool> WaitSynchronization() { - LOG_ERROR(Kernel, "(UNIMPLEMENTED)"); - return UnimplementedFunction(ErrorModule::Kernel); + bool IsWaitable() const { + switch (GetHandleType()) { + case HandleType::Session: + case HandleType::Event: + case HandleType::Mutex: + case HandleType::Thread: + case HandleType::Semaphore: + case HandleType::Timer: + return true; + + case HandleType::Unknown: + case HandleType::Port: + case HandleType::SharedMemory: + case HandleType::Redirection: + case HandleType::Process: + case HandleType::AddressArbiter: + return false; + } + + return false; } private: friend void intrusive_ptr_add_ref(Object*); friend void intrusive_ptr_release(Object*); + static unsigned int next_object_id; + unsigned int ref_count = 0; + unsigned int object_id = next_object_id++; }; // Special functions used by boost::instrusive_ptr to do automatic ref-counting @@ -92,6 +119,45 @@ 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 current thread should wait until the object is available + * @return True if the current thread should wait due to this object being unavailable + */ + virtual bool ShouldWait() = 0; + + /// Acquire/lock the object if it is available + virtual void Acquire() = 0; + + /** + * Add a thread to wait on this object + * @param thread Pointer to thread to add + */ + 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 + */ + void RemoveWaitingThread(Thread* thread); + + /** + * Wake up the next thread waiting on this object + * @return Pointer to the thread that was resumed, nullptr if no threads are waiting + */ + SharedPtr<Thread> WakeupNextThread(); + + /// Wake up all threads waiting on this object + void WakeupAllWaitingThreads(); + +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 @@ -146,14 +212,14 @@ public: /** * Looks up a handle. - * @returns Pointer to the looked-up object, or `nullptr` if the handle is not valid. + * @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. - * @returns 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`. + * @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 { @@ -164,6 +230,19 @@ public: 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(); @@ -197,7 +276,6 @@ private: }; extern HandleTable g_handle_table; -extern SharedPtr<Thread> g_main_thread; /// The ID code of the currently running game /// TODO(Subv): This variable should not be here, diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index 853a5dd74..9f7166ca4 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -5,6 +5,8 @@ #include <map> #include <vector> +#include <boost/range/algorithm_ext/erase.hpp> + #include "common/common.h" #include "core/hle/kernel/kernel.h" @@ -13,176 +15,72 @@ namespace Kernel { -class Mutex : public Object { -public: - std::string GetTypeName() const override { return "Mutex"; } - std::string GetName() const override { return name; } - - static const HandleType HANDLE_TYPE = HandleType::Mutex; - HandleType GetHandleType() const override { return HANDLE_TYPE; } - - bool initial_locked; ///< Initial lock state when mutex was created - bool locked; ///< Current locked state - Handle lock_thread; ///< Handle to thread that currently has mutex - std::vector<Handle> waiting_threads; ///< Threads that are waiting for the mutex - std::string name; ///< Name of mutex (optional) - - ResultVal<bool> WaitSynchronization() override; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -typedef std::multimap<Handle, Handle> MutexMap; -static MutexMap g_mutex_held_locks; - -/** - * Acquires the specified mutex for the specified thread - * @param mutex Mutex that is to be acquired - * @param thread Thread that will acquired - */ -void MutexAcquireLock(Mutex* mutex, Handle thread = GetCurrentThread()->GetHandle()) { - g_mutex_held_locks.insert(std::make_pair(thread, mutex->GetHandle())); - mutex->lock_thread = thread; -} - -bool ReleaseMutexForThread(Mutex* mutex, Handle thread_handle) { - MutexAcquireLock(mutex, thread_handle); - - Thread* thread = Kernel::g_handle_table.Get<Thread>(thread_handle).get(); - if (thread == nullptr) { - LOG_ERROR(Kernel, "Called with invalid handle: %08X", thread_handle); - return false; - } - - thread->ResumeFromWait(); - return true; -} - /** * Resumes a thread waiting for the specified mutex * @param mutex The mutex that some thread is waiting on */ -void ResumeWaitingThread(Mutex* mutex) { +static void ResumeWaitingThread(Mutex* mutex) { + // Reset mutex lock thread handle, nothing is waiting + mutex->locked = false; + mutex->holding_thread = nullptr; + // Find the next waiting thread for the mutex... - if (mutex->waiting_threads.empty()) { - // Reset mutex lock thread handle, nothing is waiting - mutex->locked = false; - mutex->lock_thread = -1; - } - else { - // Resume the next waiting thread and re-lock the mutex - std::vector<Handle>::iterator iter = mutex->waiting_threads.begin(); - ReleaseMutexForThread(mutex, *iter); - mutex->waiting_threads.erase(iter); + auto next_thread = mutex->WakeupNextThread(); + if (next_thread != nullptr) { + mutex->Acquire(next_thread); } } -void MutexEraseLock(Mutex* mutex) { - Handle handle = mutex->GetHandle(); - auto locked = g_mutex_held_locks.equal_range(mutex->lock_thread); - for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - if (iter->second == handle) { - g_mutex_held_locks.erase(iter); - break; - } +void ReleaseThreadMutexes(Thread* thread) { + for (auto& mtx : thread->held_mutexes) { + ResumeWaitingThread(mtx.get()); } - mutex->lock_thread = -1; + thread->held_mutexes.clear(); } -void ReleaseThreadMutexes(Handle thread) { - auto locked = g_mutex_held_locks.equal_range(thread); - - // Release every mutex that the thread holds, and resume execution on the waiting threads - for (MutexMap::iterator iter = locked.first; iter != locked.second; ++iter) { - Mutex* mutex = g_handle_table.Get<Mutex>(iter->second).get(); - ResumeWaitingThread(mutex); - } +Mutex::Mutex() {} +Mutex::~Mutex() {} - // Erase all the locks that this thread holds - g_mutex_held_locks.erase(thread); -} +SharedPtr<Mutex> Mutex::Create(bool initial_locked, std::string name) { + SharedPtr<Mutex> mutex(new Mutex); -bool LockMutex(Mutex* mutex) { - // Mutex alread locked? - if (mutex->locked) { - return false; - } - MutexAcquireLock(mutex); - return true; -} + mutex->initial_locked = initial_locked; + mutex->locked = false; + mutex->name = std::move(name); + mutex->holding_thread = nullptr; -bool ReleaseMutex(Mutex* mutex) { - MutexEraseLock(mutex); - ResumeWaitingThread(mutex); - return true; -} + // Acquire mutex with current thread if initialized as locked... + if (initial_locked) + mutex->Acquire(); -/** - * Releases a mutex - * @param handle Handle to mutex to release - */ -ResultCode ReleaseMutex(Handle handle) { - Mutex* mutex = Kernel::g_handle_table.Get<Mutex>(handle).get(); - if (mutex == nullptr) return InvalidHandle(ErrorModule::Kernel); - - if (!ReleaseMutex(mutex)) { - // TODO(yuriks): Verify error code, this one was pulled out of thin air. I'm not even sure - // what error condition this is supposed to be signaling. - return ResultCode(ErrorDescription::AlreadyDone, ErrorModule::Kernel, - ErrorSummary::NothingHappened, ErrorLevel::Temporary); - } - return RESULT_SUCCESS; + return mutex; } -/** - * Creates a mutex - * @param handle Reference to handle for the newly created mutex - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Pointer to new Mutex object - */ -Mutex* CreateMutex(Handle& handle, bool initial_locked, const std::string& name) { - Mutex* mutex = new Mutex; - // TODO(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(mutex).ValueOr(INVALID_HANDLE); +bool Mutex::ShouldWait() { + return locked && holding_thread != GetCurrentThread(); +} - mutex->locked = mutex->initial_locked = initial_locked; - mutex->name = name; +void Mutex::Acquire() { + Acquire(GetCurrentThread()); +} - // Acquire mutex with current thread if initialized as locked... - if (mutex->locked) { - MutexAcquireLock(mutex); +void Mutex::Acquire(SharedPtr<Thread> thread) { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + if (locked) + return; - // Otherwise, reset lock thread handle - } else { - mutex->lock_thread = -1; - } - return mutex; -} + locked = true; -/** - * Creates a mutex - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Handle to newly created object - */ -Handle CreateMutex(bool initial_locked, const std::string& name) { - Handle handle; - Mutex* mutex = CreateMutex(handle, initial_locked, name); - return handle; + thread->held_mutexes.insert(this); + holding_thread = std::move(thread); } -ResultVal<bool> Mutex::WaitSynchronization() { - bool wait = locked; - if (locked) { - waiting_threads.push_back(GetCurrentThread()->GetHandle()); - Kernel::WaitCurrentThread(WAITTYPE_MUTEX, this); - } else { - // Lock the mutex when the first thread accesses it - locked = true; - MutexAcquireLock(this); - } +void Mutex::Release() { + if (!locked) + return; - return MakeResult<bool>(wait); + holding_thread->held_mutexes.erase(this); + ResumeWaitingThread(this); } + } // namespace diff --git a/src/core/hle/kernel/mutex.h b/src/core/hle/kernel/mutex.h index a8ca97014..548403614 100644 --- a/src/core/hle/kernel/mutex.h +++ b/src/core/hle/kernel/mutex.h @@ -4,30 +4,57 @@ #pragma once +#include <string> + #include "common/common_types.h" #include "core/hle/kernel/kernel.h" namespace Kernel { -/** - * Releases a mutex - * @param handle Handle to mutex to release - */ -ResultCode ReleaseMutex(Handle handle); - -/** - * Creates a mutex - * @param initial_locked Specifies if the mutex should be locked initially - * @param name Optional name of mutex - * @return Handle to newly created object - */ -Handle CreateMutex(bool initial_locked, const std::string& name="Unknown"); +class Thread; + +class Mutex final : public WaitObject { +public: + /** + * Creates a mutex. + * @param initial_locked Specifies if the mutex should be locked initially + * @param name Optional name of mutex + * @return Pointer to new Mutex object + */ + static SharedPtr<Mutex> Create(bool initial_locked, std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Mutex"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Mutex; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + bool initial_locked; ///< Initial lock state when mutex was created + bool locked; ///< Current locked state + std::string name; ///< Name of mutex (optional) + SharedPtr<Thread> holding_thread; ///< Thread that has acquired the mutex + + bool ShouldWait() override; + void Acquire() override; + + /** + * Acquires the specified mutex for the specified thread + * @param mutex Mutex that is to be acquired + * @param thread Thread that will acquire the mutex + */ + void Acquire(SharedPtr<Thread> thread); + void Release(); + +private: + Mutex(); + ~Mutex() override; +}; /** * Releases all the mutexes held by the specified thread * @param thread Thread that is holding the mutexes */ -void ReleaseThreadMutexes(Handle thread); +void ReleaseThreadMutexes(Thread* thread); } // namespace diff --git a/src/core/hle/kernel/semaphore.cpp b/src/core/hle/kernel/semaphore.cpp index 88ec9a104..c8cf8b9a2 100644 --- a/src/core/hle/kernel/semaphore.cpp +++ b/src/core/hle/kernel/semaphore.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <queue> - #include "common/common.h" #include "core/hle/kernel/kernel.h" @@ -12,86 +10,51 @@ namespace Kernel { -class Semaphore : public Object { -public: - std::string GetTypeName() const override { return "Semaphore"; } - std::string GetName() const override { return name; } - - static const HandleType HANDLE_TYPE = HandleType::Semaphore; - HandleType GetHandleType() const override { return HANDLE_TYPE; } - - s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have - s32 available_count; ///< Number of free slots left in the semaphore - std::queue<Handle> waiting_threads; ///< Threads that are waiting for the semaphore - std::string name; ///< Name of semaphore (optional) - - /** - * Tests whether a semaphore still has free slots - * @return Whether the semaphore is available - */ - bool IsAvailable() const { - return available_count > 0; - } - - ResultVal<bool> WaitSynchronization() override { - bool wait = !IsAvailable(); - - if (wait) { - Kernel::WaitCurrentThread(WAITTYPE_SEMA, this); - waiting_threads.push(GetCurrentThread()->GetHandle()); - } else { - --available_count; - } - - return MakeResult<bool>(wait); - } -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// +Semaphore::Semaphore() {} +Semaphore::~Semaphore() {} -ResultCode CreateSemaphore(Handle* handle, s32 initial_count, - s32 max_count, const std::string& name) { +ResultVal<SharedPtr<Semaphore>> Semaphore::Create(s32 initial_count, s32 max_count, + std::string name) { if (initial_count > max_count) return ResultCode(ErrorDescription::InvalidCombination, ErrorModule::Kernel, ErrorSummary::WrongArgument, ErrorLevel::Permanent); - Semaphore* semaphore = new Semaphore; - // TOOD(yuriks): Fix error reporting - *handle = g_handle_table.Create(semaphore).ValueOr(INVALID_HANDLE); + SharedPtr<Semaphore> semaphore(new Semaphore); // When the semaphore is created, some slots are reserved for other threads, // and the rest is reserved for the caller thread semaphore->max_count = max_count; semaphore->available_count = initial_count; - semaphore->name = name; + semaphore->name = std::move(name); - return RESULT_SUCCESS; + return MakeResult<SharedPtr<Semaphore>>(std::move(semaphore)); } -ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { - Semaphore* semaphore = g_handle_table.Get<Semaphore>(handle).get(); - if (semaphore == nullptr) - return InvalidHandle(ErrorModule::Kernel); +bool Semaphore::ShouldWait() { + return available_count <= 0; +} + +void Semaphore::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + --available_count; +} - if (semaphore->max_count - semaphore->available_count < release_count) +ResultVal<s32> Semaphore::Release(s32 release_count) { + if (max_count - available_count < release_count) return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); - *count = semaphore->available_count; - semaphore->available_count += release_count; + s32 previous_count = available_count; + available_count += release_count; // Notify some of the threads that the semaphore has been released // stop once the semaphore is full again or there are no more waiting threads - while (!semaphore->waiting_threads.empty() && semaphore->IsAvailable()) { - Thread* thread = Kernel::g_handle_table.Get<Thread>(semaphore->waiting_threads.front()).get(); - if (thread != nullptr) - thread->ResumeFromWait(); - semaphore->waiting_threads.pop(); - --semaphore->available_count; + while (!ShouldWait() && WakeupNextThread() != nullptr) { + Acquire(); } - return RESULT_SUCCESS; + return MakeResult<s32>(previous_count); } } // namespace diff --git a/src/core/hle/kernel/semaphore.h b/src/core/hle/kernel/semaphore.h index 8644ecf0c..d8dc1fd78 100644 --- a/src/core/hle/kernel/semaphore.h +++ b/src/core/hle/kernel/semaphore.h @@ -4,29 +4,51 @@ #pragma once +#include <queue> +#include <string> + #include "common/common_types.h" #include "core/hle/kernel/kernel.h" namespace Kernel { -/** - * Creates a semaphore. - * @param handle Pointer to the handle of the newly created object - * @param initial_count Number of slots reserved for other threads - * @param max_count Maximum number of slots the semaphore can have - * @param name Optional name of semaphore - * @return ResultCode of the error - */ -ResultCode CreateSemaphore(Handle* handle, s32 initial_count, s32 max_count, const std::string& name = "Unknown"); - -/** - * Releases a certain number of slots from a semaphore. - * @param count The number of free slots the semaphore had before this call - * @param handle The handle of the semaphore to release - * @param release_count The number of slots to release - * @return ResultCode of the error - */ -ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count); +class Semaphore final : public WaitObject { +public: + /** + * Creates a semaphore. + * @param handle Pointer to the handle of the newly created object + * @param initial_count Number of slots reserved for other threads + * @param max_count Maximum number of slots the semaphore can have + * @param name Optional name of semaphore + * @return The created semaphore + */ + static ResultVal<SharedPtr<Semaphore>> Create(s32 initial_count, s32 max_count, + std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Semaphore"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Semaphore; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + s32 max_count; ///< Maximum number of simultaneous holders the semaphore can have + s32 available_count; ///< Number of free slots left in the semaphore + std::string name; ///< Name of semaphore (optional) + + bool ShouldWait() override; + void Acquire() override; + + /** + * Releases a certain number of slots from a semaphore. + * @param release_count The number of slots to release + * @return The number of free slots the semaphore had before this call + */ + ResultVal<s32> Release(s32 release_count); + +private: + Semaphore(); + ~Semaphore() override; +}; } // namespace diff --git a/src/core/hle/kernel/session.cpp b/src/core/hle/kernel/session.cpp new file mode 100644 index 000000000..0594967f8 --- /dev/null +++ b/src/core/hle/kernel/session.cpp @@ -0,0 +1,13 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/kernel/session.h" +#include "core/hle/kernel/thread.h" + +namespace Kernel { + +Session::Session() {} +Session::~Session() {} + +} diff --git a/src/core/hle/kernel/session.h b/src/core/hle/kernel/session.h index 91f3ffc2c..7cc9332c9 100644 --- a/src/core/hle/kernel/session.h +++ b/src/core/hle/kernel/session.h @@ -5,6 +5,7 @@ #pragma once #include "core/hle/kernel/kernel.h" +#include "core/mem_map.h" namespace Kernel { @@ -41,8 +42,11 @@ inline static u32* GetCommandBuffer(const int offset=0) { * CTR-OS so that IPC calls can be optionally handled by the real implementations of processes, as * opposed to HLE simulations. */ -class Session : public Object { +class Session : public WaitObject { public: + Session(); + ~Session() override; + std::string GetTypeName() const override { return "Session"; } static const HandleType HANDLE_TYPE = HandleType::Session; @@ -53,6 +57,17 @@ public: * aren't supported yet. */ virtual ResultVal<bool> SyncRequest() = 0; + + // TODO(bunnei): These functions exist to satisfy a hardware test with a Session object + // passed into WaitSynchronization. Figure out the meaning of them. + + bool ShouldWait() override { + return true; + } + + void Acquire() override { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); + } }; } diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 5368e4728..4211fcf04 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -9,76 +9,40 @@ namespace Kernel { -class SharedMemory : public Object { -public: - std::string GetTypeName() const override { return "SharedMemory"; } +SharedMemory::SharedMemory() {} +SharedMemory::~SharedMemory() {} - static const HandleType HANDLE_TYPE = HandleType::SharedMemory; - HandleType GetHandleType() const override { return HANDLE_TYPE; } +SharedPtr<SharedMemory> SharedMemory::Create(std::string name) { + SharedPtr<SharedMemory> shared_memory(new SharedMemory); - u32 base_address; ///< Address of shared memory block in RAM - MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) - MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) - std::string name; ///< Name of shared memory object (optional) -}; + shared_memory->name = std::move(name); -//////////////////////////////////////////////////////////////////////////////////////////////////// - -/** - * Creates a shared memory object - * @param handle Handle of newly created shared memory object - * @param name Name of shared memory object - * @return Pointer to newly created shared memory object - */ -SharedMemory* CreateSharedMemory(Handle& handle, const std::string& name) { - SharedMemory* shared_memory = new SharedMemory; - // TOOD(yuriks): Fix error reporting - handle = Kernel::g_handle_table.Create(shared_memory).ValueOr(INVALID_HANDLE); - shared_memory->name = name; return shared_memory; } -Handle CreateSharedMemory(const std::string& name) { - Handle handle; - CreateSharedMemory(handle, name); - return handle; -} - -/** - * Maps a shared memory block to an address in system memory - * @param handle Shared memory block handle - * @param address Address in system memory to map shared memory block to - * @param permissions Memory block map permissions (specified by SVC field) - * @param other_permissions Memory block map other permissions (specified by SVC field) - * @return Result of operation, 0 on success, otherwise error code - */ -ResultCode MapSharedMemory(u32 handle, u32 address, MemoryPermission permissions, - MemoryPermission other_permissions) { +ResultCode SharedMemory::Map(VAddr address, MemoryPermission permissions, + MemoryPermission other_permissions) { if (address < Memory::SHARED_MEMORY_VADDR || address >= Memory::SHARED_MEMORY_VADDR_END) { - LOG_ERROR(Kernel_SVC, "cannot map handle=0x%08X, address=0x%08X outside of shared mem bounds!", - handle, address); + LOG_ERROR(Kernel, "cannot map id=%u, address=0x%08X outside of shared mem bounds!", + GetObjectId(), address); + // TODO: Verify error code with hardware return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); } - SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); - if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); - shared_memory->base_address = address; - shared_memory->permissions = permissions; - shared_memory->other_permissions = other_permissions; + this->base_address = address; + this->permissions = permissions; + this->other_permissions = other_permissions; return RESULT_SUCCESS; } -ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset) { - SharedMemory* shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle).get(); - if (shared_memory == nullptr) return InvalidHandle(ErrorModule::Kernel); - - if (0 != shared_memory->base_address) - return MakeResult<u8*>(Memory::GetPointer(shared_memory->base_address + offset)); +ResultVal<u8*> SharedMemory::GetPointer(u32 offset) { + if (base_address != 0) + return MakeResult<u8*>(Memory::GetPointer(base_address + offset)); - LOG_ERROR(Kernel_SVC, "memory block handle=0x%08X not mapped!", handle); + LOG_ERROR(Kernel_SVC, "memory block id=%u not mapped!", GetObjectId()); // TODO(yuriks): Verify error code. return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, ErrorSummary::InvalidState, ErrorLevel::Permanent); diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index bb65c7ccd..5833b411c 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -23,29 +23,42 @@ enum class MemoryPermission : u32 { DontCare = (1u << 28) }; -/** - * Creates a shared memory object - * @param name Optional name of shared memory object - * @return Handle of newly created shared memory object - */ -Handle CreateSharedMemory(const std::string& name="Unknown"); - -/** - * Maps a shared memory block to an address in system memory - * @param handle Shared memory block handle - * @param address Address in system memory to map shared memory block to - * @param permissions Memory block map permissions (specified by SVC field) - * @param other_permissions Memory block map other permissions (specified by SVC field) - */ -ResultCode MapSharedMemory(Handle handle, u32 address, MemoryPermission permissions, - MemoryPermission other_permissions); - -/** - * Gets a pointer to the shared memory block - * @param handle Shared memory block handle - * @param offset Offset from the start of the shared memory block to get pointer - * @return Pointer to the shared memory block from the specified offset - */ -ResultVal<u8*> GetSharedMemoryPointer(Handle handle, u32 offset); +class SharedMemory final : public Object { +public: + /** + * Creates a shared memory object + * @param name Optional object name, used only for debugging purposes. + */ + static SharedPtr<SharedMemory> Create(std::string name = "Unknown"); + + std::string GetTypeName() const override { return "SharedMemory"; } + + static const HandleType HANDLE_TYPE = HandleType::SharedMemory; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + /** + * Maps a shared memory block to an address in system memory + * @param address Address in system memory to map shared memory block to + * @param permissions Memory block map permissions (specified by SVC field) + * @param other_permissions Memory block map other permissions (specified by SVC field) + */ + ResultCode Map(VAddr address, MemoryPermission permissions, MemoryPermission other_permissions); + + /** + * Gets a pointer to the shared memory block + * @param offset Offset from the start of the shared memory block to get pointer + * @return Pointer to the shared memory block from the specified offset + */ + ResultVal<u8*> GetPointer(u32 offset = 0); + + VAddr base_address; ///< Address of shared memory block in RAM + MemoryPermission permissions; ///< Permissions of shared memory block (SVC field) + MemoryPermission other_permissions; ///< Other permissions of shared memory block (SVC field) + std::string name; ///< Name of shared memory object (optional) + +private: + SharedMemory(); + ~SharedMemory() override; +}; } // namespace diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index dd20ca30e..3987f9608 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -4,7 +4,6 @@ #include <algorithm> #include <list> -#include <map> #include <vector> #include "common/common.h" @@ -22,17 +21,12 @@ namespace Kernel { -ResultVal<bool> Thread::WaitSynchronization() { - const bool wait = status != THREADSTATUS_DORMANT; - if (wait) { - Thread* thread = GetCurrentThread(); - if (std::find(waiting_threads.begin(), waiting_threads.end(), thread) == waiting_threads.end()) { - waiting_threads.push_back(thread); - } - WaitCurrentThread(WAITTYPE_THREADEND, this); - } +bool Thread::ShouldWait() { + return status != THREADSTATUS_DORMANT; +} - return MakeResult<bool>(wait); +void Thread::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } // Lists all thread ids that aren't deleted/etc. @@ -46,6 +40,9 @@ static Thread* current_thread; static const u32 INITIAL_THREAD_ID = 1; ///< The first available thread id at startup static u32 next_thread_id; ///< The next available thread id +Thread::Thread() {} +Thread::~Thread() {} + Thread* GetCurrentThread() { return current_thread; } @@ -55,7 +52,7 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { memset(&t->context, 0, sizeof(Core::ThreadContext)); t->context.cpu_registers[0] = arg; - t->context.pc = t->context.reg_15 = t->entry_point; + t->context.pc = t->entry_point; t->context.sp = t->stack_top; t->context.cpsr = 0x1F; // Usermode @@ -67,8 +64,8 @@ static void ResetThread(Thread* t, u32 arg, s32 lowest_priority) { if (t->current_priority < lowest_priority) { t->current_priority = t->initial_priority; } - t->wait_type = WAITTYPE_NONE; - t->wait_object = nullptr; + + t->wait_objects.clear(); t->wait_address = 0; } @@ -88,37 +85,35 @@ static void ChangeReadyState(Thread* t, bool ready) { } } -/// Check if a thread is blocking on a specified wait type -static bool CheckWaitType(const Thread* thread, WaitType type) { - return (type == thread->wait_type) && (thread->IsWaiting()); -} +/// Check if a thread is waiting on a the specified wait object +static bool CheckWait_WaitObject(const Thread* thread, WaitObject* wait_object) { + auto itr = std::find(thread->wait_objects.begin(), thread->wait_objects.end(), wait_object); + + if (itr != thread->wait_objects.end()) + return thread->IsWaiting(); -/// Check if a thread is blocking on a specified wait type with a specified handle -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object) { - return CheckWaitType(thread, type) && wait_object == thread->wait_object; + return false; } -/// Check if a thread is blocking on a specified wait type with a specified handle and address -static bool CheckWaitType(const Thread* thread, WaitType type, Object* wait_object, VAddr wait_address) { - return CheckWaitType(thread, type, wait_object) && (wait_address == thread->wait_address); +/// Check if the specified thread is waiting on the specified address to be arbitrated +static bool CheckWait_AddressArbiter(const Thread* thread, VAddr wait_address) { + return thread->IsWaiting() && thread->wait_objects.empty() && wait_address == thread->wait_address; } /// Stops the current thread void Thread::Stop(const char* reason) { // Release all the mutexes that this thread holds - ReleaseThreadMutexes(GetHandle()); + ReleaseThreadMutexes(this); ChangeReadyState(this, false); status = THREADSTATUS_DORMANT; - for (auto& waiting_thread : waiting_threads) { - if (CheckWaitType(waiting_thread.get(), WAITTYPE_THREADEND, this)) - waiting_thread->ResumeFromWait(); - } - waiting_threads.clear(); + WakeupAllWaitingThreads(); // Stopped threads are never waiting. - wait_type = WAITTYPE_NONE; - wait_object = nullptr; + for (auto& wait_object : wait_objects) { + wait_object->RemoveWaitingThread(this); + } + wait_objects.clear(); wait_address = 0; } @@ -129,26 +124,20 @@ static void ChangeThreadState(Thread* t, ThreadStatus new_status) { } ChangeReadyState(t, (new_status & THREADSTATUS_READY) != 0); t->status = new_status; - - if (new_status == THREADSTATUS_WAIT) { - if (t->wait_type == WAITTYPE_NONE) { - LOG_ERROR(Kernel, "Waittype none not allowed"); - } - } } /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { +Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; s32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (!CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + if (!CheckWait_AddressArbiter(thread.get(), address)) continue; if (thread == nullptr) - continue; // TODO(yuriks): Thread handle will hang around forever. Should clean up. + continue; if(thread->current_priority <= priority) { highest_priority_thread = thread.get(); @@ -165,11 +154,11 @@ Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address) { } /// Arbitrate all threads currently waiting -void ArbitrateAllThreads(Object* arbiter, u32 address) { +void ArbitrateAllThreads(u32 address) { // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { - if (CheckWaitType(thread.get(), WAITTYPE_ARB, arbiter, address)) + if (CheckWait_AddressArbiter(thread.get(), address)) thread->ResumeFromWait(); } } @@ -177,9 +166,6 @@ void ArbitrateAllThreads(Object* arbiter, u32 address) { /// Calls a thread by marking it as "ready" (note: will not actually execute until current thread yields) static void CallThread(Thread* t) { // Stop waiting - if (t->wait_type != WAITTYPE_NONE) { - t->wait_type = WAITTYPE_NONE; - } ChangeThreadState(t, THREADSTATUS_READY); } @@ -200,7 +186,6 @@ static void SwitchContext(Thread* t) { current_thread = t; ChangeReadyState(t, false); t->status = (t->status | THREADSTATUS_RUNNING) & ~THREADSTATUS_READY; - t->wait_type = WAITTYPE_NONE; Core::g_app_core->LoadContext(t->context); } else { current_thread = nullptr; @@ -223,49 +208,119 @@ static Thread* NextThread() { return next; } -void WaitCurrentThread(WaitType wait_type, Object* wait_object) { +void WaitCurrentThread_Sleep() { Thread* thread = GetCurrentThread(); - thread->wait_type = wait_type; - thread->wait_object = wait_object; ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } -void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address) { - WaitCurrentThread(wait_type, wait_object); - GetCurrentThread()->wait_address = wait_address; +void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all) { + Thread* thread = GetCurrentThread(); + thread->wait_set_output = wait_set_output; + thread->wait_all = wait_all; + + // It's possible to call WaitSynchronizationN without any objects passed in... + if (wait_object != nullptr) + thread->wait_objects.push_back(wait_object); + + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); +} + +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address) { + Thread* thread = GetCurrentThread(); + thread->wait_address = wait_address; + ChangeThreadState(thread, ThreadStatus(THREADSTATUS_WAIT | (thread->status & THREADSTATUS_SUSPEND))); } /// Event type for the thread wake up event static int ThreadWakeupEventType = -1; +// TODO(yuriks): This can be removed if Thread objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable wakeup_callback_handle_table; /// Callback that will wake up the thread it was scheduled for -static void ThreadWakeupCallback(u64 parameter, int cycles_late) { - Handle handle = static_cast<Handle>(parameter); - SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(handle); +static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { + SharedPtr<Thread> thread = wakeup_callback_handle_table.Get<Thread>((Handle)thread_handle); if (thread == nullptr) { - LOG_ERROR(Kernel, "Thread doesn't exist %u", handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid thread %08X", thread_handle); return; } + thread->SetWaitSynchronizationResult(ResultCode(ErrorDescription::Timeout, ErrorModule::OS, + ErrorSummary::StatusChanged, ErrorLevel::Info)); + + if (thread->wait_set_output) + thread->SetWaitSynchronizationOutput(-1); + thread->ResumeFromWait(); } -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds) { +void Thread::WakeAfterDelay(s64 nanoseconds) { // Don't schedule a wakeup if the thread wants to wait forever if (nanoseconds == -1) return; - _dbg_assert_(Kernel, thread != nullptr); u64 microseconds = nanoseconds / 1000; - CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, thread->GetHandle()); + CoreTiming::ScheduleEvent(usToCycles(microseconds), ThreadWakeupEventType, callback_handle); +} + +void Thread::ReleaseWaitObject(WaitObject* wait_object) { + if (wait_objects.empty()) { + LOG_CRITICAL(Kernel, "thread is not waiting on any objects!"); + return; + } + + // Remove this thread from the waiting object's thread list + wait_object->RemoveWaitingThread(this); + + unsigned index = 0; + bool wait_all_failed = false; // Will be set to true if any object is unavailable + + // Iterate through all waiting objects to check availability... + for (auto itr = wait_objects.begin(); itr != wait_objects.end(); ++itr) { + if ((*itr)->ShouldWait()) + wait_all_failed = true; + + // The output should be the last index of wait_object + if (*itr == wait_object) + index = itr - wait_objects.begin(); + } + + // If we are waiting on all objects... + if (wait_all) { + // Resume the thread only if all are available... + if (!wait_all_failed) { + SetWaitSynchronizationResult(RESULT_SUCCESS); + SetWaitSynchronizationOutput(-1); + + ResumeFromWait(); + } + } else { + // Otherwise, resume + SetWaitSynchronizationResult(RESULT_SUCCESS); + + if (wait_set_output) + SetWaitSynchronizationOutput(index); + + ResumeFromWait(); + } } -/// Resumes a thread from waiting by marking it as "ready" void Thread::ResumeFromWait() { + // Cancel any outstanding wakeup events + CoreTiming::UnscheduleEvent(ThreadWakeupEventType, callback_handle); + status &= ~THREADSTATUS_WAIT; - wait_object = nullptr; - wait_type = WAITTYPE_NONE; + + // Remove this thread from all other WaitObjects + for (auto wait_object : wait_objects) + wait_object->RemoveWaitingThread(this); + + wait_objects.clear(); + wait_set_output = false; + wait_all = false; + wait_address = 0; + if (!(status & (THREADSTATUS_WAITSUSPEND | THREADSTATUS_DORMANT | THREADSTATUS_DEAD))) { ChangeReadyState(this, true); } @@ -277,11 +332,11 @@ static void DebugThreadQueue() { if (!thread) { return; } - LOG_DEBUG(Kernel, "0x%02X 0x%08X (current)", thread->current_priority, GetCurrentThread()->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u (current)", thread->current_priority, GetCurrentThread()->GetObjectId()); for (auto& t : thread_list) { s32 priority = thread_ready_queue.contains(t.get()); if (priority != -1) { - LOG_DEBUG(Kernel, "0x%02X 0x%08X", priority, t->GetHandle()); + LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); } } } @@ -313,14 +368,6 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, SharedPtr<Thread> thread(new Thread); - // TODO(yuriks): Thread requires a handle to be inserted into the various scheduling queues for - // the time being. Create a handle here, it will be copied to the handle field in - // the object and use by the rest of the code. This should be removed when other - // code doesn't rely on the handle anymore. - ResultVal<Handle> handle = Kernel::g_handle_table.Create(thread); - if (handle.Failed()) - return handle.Code(); - thread_list.push_back(thread); thread_ready_queue.prepare(priority); @@ -331,10 +378,12 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->stack_size = stack_size; thread->initial_priority = thread->current_priority = priority; thread->processor_id = processor_id; - thread->wait_type = WAITTYPE_NONE; - thread->wait_object = nullptr; + thread->wait_set_output = false; + thread->wait_all = false; + thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); + thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); ResetThread(thread.get(), arg, 0); CallThread(thread.get()); @@ -368,16 +417,14 @@ void Thread::SetPriority(s32 priority) { } } -Handle SetupIdleThread() { +SharedPtr<Thread> SetupIdleThread() { // We need to pass a few valid values to get around parameter checking in Thread::Create. - auto thread_res = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, - THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE); - _dbg_assert_(Kernel, thread_res.Succeeded()); - SharedPtr<Thread> thread = std::move(*thread_res); + auto thread = Thread::Create("idle", Memory::KERNEL_MEMORY_VADDR, THREADPRIO_LOWEST, 0, + THREADPROCESSORID_0, 0, Kernel::DEFAULT_STACK_SIZE).MoveFrom(); thread->idle = true; CallThread(thread.get()); - return thread->GetHandle(); + return thread; } SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size) { @@ -410,19 +457,26 @@ void Reschedule() { HLE::g_reschedule = false; if (next != nullptr) { - LOG_TRACE(Kernel, "context switch 0x%08X -> 0x%08X", prev->GetHandle(), next->GetHandle()); + LOG_TRACE(Kernel, "context switch %u -> %u", prev->GetObjectId(), next->GetObjectId()); SwitchContext(next); } else { - LOG_TRACE(Kernel, "cannot context switch from 0x%08X, no higher priority thread!", prev->GetHandle()); + LOG_TRACE(Kernel, "cannot context switch from %u, no higher priority thread!", prev->GetObjectId()); for (auto& thread : thread_list) { - LOG_TRACE(Kernel, "\thandle=0x%08X prio=0x%02X, status=0x%08X wait_type=0x%08X wait_handle=0x%08X", - thread->GetHandle(), thread->current_priority, thread->status, thread->wait_type, - (thread->wait_object ? thread->wait_object->GetHandle() : INVALID_HANDLE)); + LOG_TRACE(Kernel, "\tid=%u prio=0x%02X, status=0x%08X", thread->GetObjectId(), + thread->current_priority, thread->status); } } } +void Thread::SetWaitSynchronizationResult(ResultCode result) { + context.cpu_registers[0] = result.raw; +} + +void Thread::SetWaitSynchronizationOutput(s32 output) { + context.cpu_registers[1] = output; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 284dec400..633bb7c98 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -7,6 +7,8 @@ #include <string> #include <vector> +#include <boost/container/flat_set.hpp> + #include "common/common_types.h" #include "core/core.h" @@ -38,21 +40,11 @@ enum ThreadStatus { THREADSTATUS_WAITSUSPEND = THREADSTATUS_WAIT | THREADSTATUS_SUSPEND }; -enum WaitType { - WAITTYPE_NONE, - WAITTYPE_SLEEP, - WAITTYPE_SEMA, - WAITTYPE_EVENT, - WAITTYPE_THREADEND, - WAITTYPE_MUTEX, - WAITTYPE_SYNCH, - WAITTYPE_ARB, - WAITTYPE_TIMER, -}; - namespace Kernel { -class Thread : public Kernel::Object { +class Mutex; + +class Thread final : public WaitObject { public: static ResultVal<SharedPtr<Thread>> Create(std::string name, VAddr entry_point, s32 priority, u32 arg, s32 processor_id, VAddr stack_top, u32 stack_size); @@ -70,7 +62,8 @@ public: inline bool IsSuspended() const { return (status & THREADSTATUS_SUSPEND) != 0; } inline bool IsIdle() const { return idle; } - ResultVal<bool> WaitSynchronization() override; + bool ShouldWait() override; + void Acquire() override; s32 GetPriority() const { return current_priority; } void SetPriority(s32 priority); @@ -78,9 +71,34 @@ public: u32 GetThreadId() const { return thread_id; } void Stop(const char* reason); - /// Resumes a thread from waiting by marking it as "ready". + + /** + * Release an acquired wait object + * @param wait_object WaitObject to release + */ + void ReleaseWaitObject(WaitObject* wait_object); + + /// Resumes a thread from waiting by marking it as "ready" void ResumeFromWait(); + /** + * Schedules an event to wake up the specified thread after the specified delay. + * @param nanoseconds The time this thread will be allowed to sleep for. + */ + void WakeAfterDelay(s64 nanoseconds); + + /** + * Sets the result after the thread awakens (from either WaitSynchronization SVC) + * @param result Value to set to the returned result + */ + void SetWaitSynchronizationResult(ResultCode result); + + /** + * Sets the output parameter value after the thread awakens (from WaitSynchronizationN SVC only) + * @param output Value to set to the output parameter + */ + void SetWaitSynchronizationOutput(s32 output); + Core::ThreadContext context; u32 thread_id; @@ -95,11 +113,13 @@ public: s32 processor_id; - WaitType wait_type; - Object* wait_object; - VAddr wait_address; + /// Mutexes currently held by this thread, which will be released when it exits. + boost::container::flat_set<SharedPtr<Mutex>> held_mutexes; - std::vector<SharedPtr<Thread>> waiting_threads; + std::vector<SharedPtr<WaitObject>> wait_objects; ///< Objects that the thread is waiting on + VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address + bool wait_all; ///< True if the thread is waiting on all objects before resuming + bool wait_set_output; ///< True if the output parameter should be set on thread wakeup std::string name; @@ -107,9 +127,15 @@ public: bool idle = false; private: - Thread() = default; + Thread(); + ~Thread() override; + + /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. + Handle callback_handle; }; +extern SharedPtr<Thread> g_main_thread; + /// Sets up the primary application thread SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); @@ -117,37 +143,30 @@ SharedPtr<Thread> SetupMainThread(s32 priority, u32 stack_size); void Reschedule(); /// Arbitrate the highest priority thread that is waiting -Thread* ArbitrateHighestPriorityThread(Object* arbiter, u32 address); +Thread* ArbitrateHighestPriorityThread(u32 address); /// Arbitrate all threads currently waiting... -void ArbitrateAllThreads(Object* arbiter, u32 address); +void ArbitrateAllThreads(u32 address); /// Gets the current thread Thread* GetCurrentThread(); -/** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_object Kernel object that we are waiting on, defaults to current thread - */ -void WaitCurrentThread(WaitType wait_type, Object* wait_object = GetCurrentThread()); +/// Waits the current thread on a sleep +void WaitCurrentThread_Sleep(); /** - * Schedules an event to wake up the specified thread after the specified delay. - * @param handle The thread handle. - * @param nanoseconds The time this thread will be allowed to sleep for. + * Waits the current thread from a WaitSynchronization call + * @param wait_object Kernel object that we are waiting on + * @param wait_set_output If true, set the output parameter on thread wakeup (for WaitSynchronizationN only) + * @param wait_all If true, wait on all objects before resuming (for WaitSynchronizationN only) */ -void WakeThreadAfterDelay(Thread* thread, s64 nanoseconds); +void WaitCurrentThread_WaitSynchronization(SharedPtr<WaitObject> wait_object, bool wait_set_output, bool wait_all); /** - * Puts the current thread in the wait state for the given type - * @param wait_type Type of wait - * @param wait_object Kernel object that we are waiting on + * Waits the current thread from an ArbitrateAddress call * @param wait_address Arbitration address used to resume from wait */ -void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_address); - - +void WaitCurrentThread_ArbitrateAddress(VAddr wait_address); /** * Sets up the idle thread, this is a thread that is intended to never execute instructions, @@ -155,7 +174,8 @@ void WaitCurrentThread(WaitType wait_type, Object* wait_object, VAddr wait_addre * and will try to yield on every call. * @returns The handle of the idle thread */ -Handle SetupIdleThread(); +SharedPtr<Thread> SetupIdleThread(); + /// Initialize threading void ThreadingInit(); diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 3b0452d4d..4352fc99c 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -2,8 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include <set> - #include "common/common.h" #include "core/core_timing.h" @@ -13,77 +11,62 @@ namespace Kernel { -class Timer : public Object { -public: - std::string GetTypeName() const override { return "Timer"; } - std::string GetName() const override { return name; } - - static const HandleType HANDLE_TYPE = HandleType::Timer; - HandleType GetHandleType() const override { return HANDLE_TYPE; } - - ResetType reset_type; ///< The ResetType of this timer - - bool signaled; ///< Whether the timer has been signaled or not - std::set<Handle> waiting_threads; ///< Threads that are waiting for the timer - std::string name; ///< Name of timer (optional) - - u64 initial_delay; ///< The delay until the timer fires for the first time - u64 interval_delay; ///< The delay until the timer fires after the first time - - ResultVal<bool> WaitSynchronization() override { - bool wait = !signaled; - if (wait) { - waiting_threads.insert(GetCurrentThread()->GetHandle()); - Kernel::WaitCurrentThread(WAITTYPE_TIMER, this); - } - return MakeResult<bool>(wait); - } -}; +/// The event type of the generic timer callback event +static int timer_callback_event_type = -1; +// TODO(yuriks): This can be removed if Timer objects are explicitly pooled in the future, allowing +// us to simply use a pool index or similar. +static Kernel::HandleTable timer_callback_handle_table; -/** - * Creates a timer. - * @param handle Reference to handle for the newly created timer - * @param reset_type ResetType describing how to create timer - * @param name Optional name of timer - * @return Newly created Timer object - */ -Timer* CreateTimer(Handle& handle, const ResetType reset_type, const std::string& name) { - Timer* timer = new Timer; +Timer::Timer() {} +Timer::~Timer() {} - handle = Kernel::g_handle_table.Create(timer).ValueOr(INVALID_HANDLE); +SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { + SharedPtr<Timer> timer(new Timer); timer->reset_type = reset_type; timer->signaled = false; - timer->name = name; + timer->name = std::move(name); timer->initial_delay = 0; timer->interval_delay = 0; + timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); + return timer; } -ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name) { - CreateTimer(*handle, reset_type, name); - return RESULT_SUCCESS; +bool Timer::ShouldWait() { + return !signaled; +} + +void Timer::Acquire() { + _assert_msg_(Kernel, !ShouldWait(), "object unavailable!"); } -ResultCode ClearTimer(Handle handle) { - SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); - - if (timer == nullptr) - return InvalidHandle(ErrorModule::Kernel); +void Timer::Set(s64 initial, s64 interval) { + // Ensure we get rid of any previous scheduled event + Cancel(); - timer->signaled = false; - return RESULT_SUCCESS; + initial_delay = initial; + interval_delay = interval; + + u64 initial_microseconds = initial / 1000; + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), + timer_callback_event_type, callback_handle); } -/// The event type of the generic timer callback event -static int TimerCallbackEventType = -1; +void Timer::Cancel() { + CoreTiming::UnscheduleEvent(timer_callback_event_type, callback_handle); +} + +void Timer::Clear() { + signaled = false; +} /// The timer callback event, called when a timer is fired static void TimerCallback(u64 timer_handle, int cycles_late) { - SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(timer_handle); + SharedPtr<Timer> timer = timer_callback_handle_table.Get<Timer>(timer_handle); if (timer == nullptr) { - LOG_CRITICAL(Kernel, "Callback fired for invalid timer %u", timer_handle); + LOG_CRITICAL(Kernel, "Callback fired for invalid timer %08X", timer_handle); return; } @@ -92,12 +75,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { timer->signaled = true; // Resume all waiting threads - for (Handle thread_handle : timer->waiting_threads) { - if (SharedPtr<Thread> thread = Kernel::g_handle_table.Get<Thread>(thread_handle)) - thread->ResumeFromWait(); - } - - timer->waiting_threads.clear(); + timer->WakeupAllWaitingThreads(); if (timer->reset_type == RESETTYPE_ONESHOT) timer->signaled = false; @@ -106,36 +84,12 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { // Reschedule the timer with the interval delay u64 interval_microseconds = timer->interval_delay / 1000; CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, - TimerCallbackEventType, timer_handle); + timer_callback_event_type, timer_handle); } } -ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { - SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); - - if (timer == nullptr) - return InvalidHandle(ErrorModule::Kernel); - - timer->initial_delay = initial; - timer->interval_delay = interval; - - u64 initial_microseconds = initial / 1000; - CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), TimerCallbackEventType, handle); - return RESULT_SUCCESS; -} - -ResultCode CancelTimer(Handle handle) { - SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); - - if (timer == nullptr) - return InvalidHandle(ErrorModule::Kernel); - - CoreTiming::UnscheduleEvent(TimerCallbackEventType, handle); - return RESULT_SUCCESS; -} - void TimersInit() { - TimerCallbackEventType = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); + timer_callback_event_type = CoreTiming::RegisterEvent("TimerCallback", TimerCallback); } void TimersShutdown() { diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index f8aa66b60..540e4e187 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -11,37 +11,54 @@ namespace Kernel { -/** - * Cancels a timer - * @param handle Handle of the timer to cancel - */ -ResultCode CancelTimer(Handle handle); - -/** - * Starts a timer with the specified initial delay and interval - * @param handle Handle of the timer to start - * @param initial Delay until the timer is first fired - * @param interval Delay until the timer is fired after the first time - */ -ResultCode SetTimer(Handle handle, s64 initial, s64 interval); - -/** - * Clears a timer - * @param handle Handle of the timer to clear - */ -ResultCode ClearTimer(Handle handle); - -/** - * Creates a timer - * @param Handle to newly created Timer object - * @param reset_type ResetType describing how to create the timer - * @param name Optional name of timer - * @return ResultCode of the error - */ -ResultCode CreateTimer(Handle* handle, const ResetType reset_type, const std::string& name="Unknown"); +class Timer final : public WaitObject { +public: + /** + * Creates a timer + * @param reset_type ResetType describing how to create the timer + * @param name Optional name of timer + * @return The created Timer + */ + static SharedPtr<Timer> Create(ResetType reset_type, std::string name = "Unknown"); + + std::string GetTypeName() const override { return "Timer"; } + std::string GetName() const override { return name; } + + static const HandleType HANDLE_TYPE = HandleType::Timer; + HandleType GetHandleType() const override { return HANDLE_TYPE; } + + ResetType reset_type; ///< The ResetType of this timer + + bool signaled; ///< Whether the timer has been signaled or not + std::string name; ///< Name of timer (optional) + + u64 initial_delay; ///< The delay until the timer fires for the first time + u64 interval_delay; ///< The delay until the timer fires after the first time + + bool ShouldWait() override; + void Acquire() override; + + /** + * Starts the timer, with the specified initial delay and interval. + * @param initial Delay until the timer is first fired + * @param interval Delay until the timer is fired after the first time + */ + void Set(s64 initial, s64 interval); + + void Cancel(); + void Clear(); + +private: + Timer(); + ~Timer() override; + + /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. + Handle callback_handle; +}; /// Initializes the required variables for timers void TimersInit(); /// Tears down the timer variables void TimersShutdown(); + } // namespace diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 82dcf5bba..948b9e38e 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -9,8 +9,9 @@ #include <type_traits> #include <utility> -#include "common/common_types.h" #include "common/bit_field.h" +#include "common/common_funcs.h" +#include "common/common_types.h" // All the constants in this file come from http://3dbrew.org/wiki/Error_codes @@ -226,11 +227,6 @@ inline ResultCode UnimplementedFunction(ErrorModule module) { return ResultCode(ErrorDescription::NotImplemented, module, ErrorSummary::NotSupported, ErrorLevel::Permanent); } -/// Returned when a function is passed an invalid handle. -inline ResultCode InvalidHandle(ErrorModule module) { - return ResultCode(ErrorDescription::InvalidHandle, module, - ErrorSummary::InvalidArgument, ErrorLevel::Permanent); -} /** * This is an optional value type. It holds a `ResultCode` and, if that code is a success code, @@ -364,6 +360,17 @@ public: return !empty() ? *GetPointer() : std::move(value); } + /// Asserts that the result succeeded and returns a reference to it. + T& Unwrap() { + // TODO(yuriks): Should be a release assert + _assert_msg_(Common, Succeeded(), "Tried to Unwrap empty ResultVal"); + return **this; + } + + T&& MoveFrom() { + return std::move(Unwrap()); + } + private: typedef typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type StorageType; @@ -400,3 +407,15 @@ template <typename T, typename... Args> ResultVal<T> MakeResult(Args&&... args) { return ResultVal<T>::WithCode(RESULT_SUCCESS, std::forward<Args>(args)...); } + +/** + * 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 + * can be used to cascade errors out, achieving something akin to exception handling. + */ +#define CASCADE_RESULT(target, source) \ + auto CONCAT2(check_result_L, __LINE__) = source; \ + if (CONCAT2(check_result_L, __LINE__).Failed()) \ + return CONCAT2(check_result_L, __LINE__).Code(); \ + target = std::move(*CONCAT2(check_result_L, __LINE__)) diff --git a/src/core/hle/service/ac_u.cpp b/src/core/hle/service/ac_u.cpp index 20a3fa2e5..53d920de1 100644 --- a/src/core/hle/service/ac_u.cpp +++ b/src/core/hle/service/ac_u.cpp @@ -53,7 +53,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/act_u.cpp b/src/core/hle/service/act_u.cpp index 10870f14b..4ea7a9fb2 100644 --- a/src/core/hle/service/act_u.cpp +++ b/src/core/hle/service/act_u.cpp @@ -18,7 +18,7 @@ namespace ACT_U { // Interface class Interface::Interface() { - //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + //Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/am_app.cpp b/src/core/hle/service/am_app.cpp index 0b396b6d3..df10db87f 100644 --- a/src/core/hle/service/am_app.cpp +++ b/src/core/hle/service/am_app.cpp @@ -18,7 +18,7 @@ namespace AM_APP { // Interface class Interface::Interface() { - //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + //Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/am_net.cpp b/src/core/hle/service/am_net.cpp index 112844e5b..c74012d9d 100644 --- a/src/core/hle/service/am_net.cpp +++ b/src/core/hle/service/am_net.cpp @@ -38,7 +38,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/am_sys.cpp b/src/core/hle/service/am_sys.cpp new file mode 100644 index 000000000..c5df8abda --- /dev/null +++ b/src/core/hle/service/am_sys.cpp @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/am_sys.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace AM_SYS + +namespace AM_SYS { + +// Empty arrays are illegal -- commented out until an entry is added. +//const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/am_sys.h b/src/core/hle/service/am_sys.h new file mode 100644 index 000000000..bb6178a43 --- /dev/null +++ b/src/core/hle/service/am_sys.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace AM_SYS + +namespace AM_SYS { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "am:sys"; + } +}; + +} // namespace diff --git a/src/core/hle/service/apt_a.cpp b/src/core/hle/service/apt_a.cpp index 37be4b027..e1dd2a5fb 100644 --- a/src/core/hle/service/apt_a.cpp +++ b/src/core/hle/service/apt_a.cpp @@ -11,6 +11,7 @@ namespace APT_U { extern void GetLockHandle(Service::Interface* self); extern void ReceiveParameter(Service::Interface* self); extern void GlanceParameter(Service::Interface* self); + extern void GetSharedFont(Service::Interface* self); } //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -29,6 +30,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000E0080, APT_U::GlanceParameter, "GlanceParameter?"}, {0x003B0040, nullptr, "CancelLibraryApplet?"}, {0x00430040, nullptr, "NotifyToWait?"}, + {0x00440000, APT_U::GetSharedFont, "GetSharedFont?"}, {0x004B00C2, nullptr, "AppletUtility?"}, {0x00550040, nullptr, "WriteInputToNsState?"}, }; @@ -37,7 +39,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/apt_s.cpp b/src/core/hle/service/apt_s.cpp new file mode 100644 index 000000000..686335428 --- /dev/null +++ b/src/core/hle/service/apt_s.cpp @@ -0,0 +1,123 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + + +#include "common/common.h" +#include "common/file_util.h" + +#include "core/hle/hle.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/mutex.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/service/apt_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace APT_S + +namespace APT_U { + extern void GetLockHandle(Service::Interface* self); + extern void Initialize(Service::Interface* self); + extern void Enable(Service::Interface* self); + extern void InquireNotification(Service::Interface* self); + extern void NotifyToWait(Service::Interface* self); + extern void GetSharedFont(Service::Interface* self); + extern void AppletUtility(Service::Interface* self); + extern void GlanceParameter(Service::Interface* self); + extern void ReceiveParameter(Service::Interface* self); +} + +namespace APT_S { + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010040, APT_U::GetLockHandle, "GetLockHandle"}, + {0x00020080, APT_U::Initialize, "Initialize"}, + {0x00030040, APT_U::Enable, "Enable"}, + {0x00040040, nullptr, "Finalize"}, + {0x00050040, nullptr, "GetAppletManInfo"}, + {0x00060040, nullptr, "GetAppletInfo"}, + {0x00070000, nullptr, "GetLastSignaledAppletId"}, + {0x00080000, nullptr, "CountRegisteredApplet"}, + {0x00090040, nullptr, "IsRegistered"}, + {0x000A0040, nullptr, "GetAttribute"}, + {0x000B0040, APT_U::InquireNotification, "InquireNotification"}, + {0x000C0104, nullptr, "SendParameter"}, + {0x000D0080, APT_U::ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, APT_U::GlanceParameter, "GlanceParameter"}, + {0x000F0100, nullptr, "CancelParameter"}, + {0x001000C2, nullptr, "DebugFunc"}, + {0x001100C0, nullptr, "MapProgramIdForDebug"}, + {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, + {0x00130000, nullptr, "GetPreparationState"}, + {0x00140040, nullptr, "SetPreparationState"}, + {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00160040, nullptr, "PreloadLibraryApplet"}, + {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, + {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00190040, nullptr, "PrepareToStartSystemApplet"}, + {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001B00C4, nullptr, "StartApplication"}, + {0x001C0000, nullptr, "WakeupApplication"}, + {0x001D0000, nullptr, "CancelApplication"}, + {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001F0084, nullptr, "StartSystemApplet"}, + {0x00200044, nullptr, "StartNewestHomeMenu"}, + {0x00210000, nullptr, "OrderToCloseApplication"}, + {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00230040, nullptr, "PrepareToJumpToApplication"}, + {0x00240044, nullptr, "JumpToApplication"}, + {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, + {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, + {0x00270044, nullptr, "CloseApplication"}, + {0x00280044, nullptr, "CloseLibraryApplet"}, + {0x00290044, nullptr, "CloseSystemApplet"}, + {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, + {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, + {0x002C0044, nullptr, "JumpToHomeMenu"}, + {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, + {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, + {0x00300044, nullptr, "LeaveResidentApplet"}, + {0x00310100, nullptr, "PrepareToDoApplicationJump"}, + {0x00320084, nullptr, "DoApplicationJump"}, + {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00340084, nullptr, "SendDeliverArg"}, + {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00360040, nullptr, "LoadSysMenuArg"}, + {0x00370042, nullptr, "StoreSysMenuArg"}, + {0x00380040, nullptr, "PreloadResidentApplet"}, + {0x00390040, nullptr, "PrepareToStartResidentApplet"}, + {0x003A0044, nullptr, "StartResidentApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003C0042, nullptr, "SendDspSleep"}, + {0x003D0042, nullptr, "SendDspWakeUp"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, + {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, + {0x00400042, nullptr, "SendCaptureBufferInfo"}, + {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, + {0x00420080, nullptr, "SleepSystem"}, + {0x00430040, APT_U::NotifyToWait, "NotifyToWait"}, + {0x00440000, APT_U::GetSharedFont, "GetSharedFont"}, + {0x00450040, nullptr, "GetWirelessRebootInfo"}, + {0x00460104, nullptr, "Wrap"}, + {0x00470104, nullptr, "Unwrap"}, + {0x00480100, nullptr, "GetProgramInfo"}, + {0x00490180, nullptr, "Reboot"}, + {0x004A0040, nullptr, "GetCaptureInfo"}, + {0x004B00C2, APT_U::AppletUtility, "AppletUtility"}, + {0x004C0000, nullptr, "SetFatalErrDispMode"}, + {0x004D0080, nullptr, "GetAppletProgramInfo"}, + {0x004E0000, nullptr, "HardwareResetAsync"}, + {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, + {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/apt_s.h b/src/core/hle/service/apt_s.h new file mode 100644 index 000000000..f097c9747 --- /dev/null +++ b/src/core/hle/service/apt_s.h @@ -0,0 +1,30 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace APT_S + +namespace APT_S { + +// Application and title launching service. These services handle signaling for home/power button as +// well. Only one session for either APT service can be open at a time, normally processes close the +// service handle immediately once finished using the service. The commands for APT:U and APT:S are +// exactly the same, however certain commands are only accessible with APT:S(NS module will call +// svcBreak when the command isn't accessible). See http://3dbrew.org/wiki/NS#APT_Services. + +/// Interface to "APT:S" service +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "APT:S"; + } +}; + +} // namespace diff --git a/src/core/hle/service/apt_u.cpp b/src/core/hle/service/apt_u.cpp index d0ff4e585..ccfd04591 100644 --- a/src/core/hle/service/apt_u.cpp +++ b/src/core/hle/service/apt_u.cpp @@ -10,7 +10,8 @@ #include "core/hle/kernel/event.h" #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" -#include "apt_u.h" +#include "core/hle/kernel/thread.h" +#include "core/hle/service/apt_u.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace APT_U @@ -25,10 +26,12 @@ namespace APT_U { // correctly mapping it in Citra, however we still do not understand how the mapping is determined. static const VAddr SHARED_FONT_VADDR = 0x18000000; -// Handle to shared memory region designated to for shared system font -static Handle shared_font_mem = 0; +/// Handle to shared memory region designated to for shared system font +static Kernel::SharedPtr<Kernel::SharedMemory> shared_font_mem; -static Handle lock_handle = 0; +static Kernel::SharedPtr<Kernel::Mutex> lock; +static Kernel::SharedPtr<Kernel::Event> notification_event; ///< APT notification event +static Kernel::SharedPtr<Kernel::Event> pause_event = 0; ///< APT pause event static std::vector<u8> shared_font; /// Signals used by APT functions @@ -39,31 +42,71 @@ enum class SignalType : u32 { ExitingApp = 0xC, }; +/// App Id's used by APT functions +enum class AppID : u32 { + HomeMenu = 0x101, + AlternateMenu = 0x103, + Camera = 0x110, + FriendsList = 0x112, + GameNotes = 0x113, + InternetBrowser = 0x114, + InstructionManual = 0x115, + Notifications = 0x116, + Miiverse = 0x117, + SoftwareKeyboard1 = 0x201, + Ed = 0x202, + PnoteApp = 0x204, + SnoteApp = 0x205, + Error = 0x206, + Mint = 0x207, + Extrapad = 0x208, + Memolib = 0x209, + Application = 0x300, + SoftwareKeyboard2 = 0x401, +}; + void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[3] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Menu"); // APT menu event handle - cmd_buff[4] = Kernel::CreateEvent(RESETTYPE_ONESHOT, "APT_U:Pause"); // APT pause event handle + // TODO(bunnei): Check if these are created in Initialize or on APT process startup. + notification_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Notification"); + pause_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "APT_U:Pause"); + + cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(pause_event).MoveFrom(); - Kernel::SetEventLocked(cmd_buff[3], true); - Kernel::SetEventLocked(cmd_buff[4], false); // Fire start event + // TODO(bunnei): Check if these events are cleared/signaled every time Initialize is called. + notification_event->Clear(); + pause_event->Signal(); // Fire start event - _assert_msg_(KERNEL, (0 != lock_handle), "Cannot initialize without lock"); - Kernel::ReleaseMutex(lock_handle); + _assert_msg_(KERNEL, (nullptr != lock), "Cannot initialize without lock"); + lock->Release(); - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error +} + +/** + * APT_U::NotifyToWait service function + * Inputs: + * 1 : AppID + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void NotifyToWait(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 app_id = cmd_buff[1]; + // TODO(Subv): Verify this, it seems to get SWKBD and Home Menu further. + pause_event->Signal(); + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); } void GetLockHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 flags = cmd_buff[1]; // TODO(bunnei): Figure out the purpose of the flag field - if (0 == lock_handle) { - // TODO(bunnei): Verify if this is created here or at application boot? - lock_handle = Kernel::CreateMutex(false, "APT_U:Lock"); - Kernel::ReleaseMutex(lock_handle); - } - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error // Not sure what these parameters are used for, but retail apps check that they are 0 after // GetLockHandle has been called. @@ -71,26 +114,102 @@ void GetLockHandle(Service::Interface* self) { cmd_buff[3] = 0; cmd_buff[4] = 0; - cmd_buff[5] = lock_handle; + cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom(); LOG_TRACE(Service_APT, "called handle=0x%08X", cmd_buff[5]); } void Enable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 unk = cmd_buff[1]; // TODO(bunnei): What is this field used for? - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); } +/** + * APT_U::GetAppletManInfo service function. + * Inputs: + * 1 : Unknown + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Unknown u32 value + * 3 : Unknown u8 value + * 4 : Home Menu AppId + * 5 : AppID of currently active app + */ +void GetAppletManInfo(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 unk = cmd_buff[1]; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0; + cmd_buff[3] = 0; + cmd_buff[4] = static_cast<u32>(AppID::HomeMenu); // Home menu AppID + cmd_buff[5] = static_cast<u32>(AppID::Application); // TODO(purpasmart96): Do this correctly + + LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); +} + +/** + * APT_U::IsRegistered service function. This returns whether the specified AppID is registered with NS yet. + * An AppID is "registered" once the process associated with the AppID uses APT:Enable. Home Menu uses this + * command to determine when the launched process is running and to determine when to stop using GSP etc, + * while displaying the "Nintendo 3DS" loading screen. + * Inputs: + * 1 : AppID + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output, 0 = not registered, 1 = registered. + */ +void IsRegistered(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 app_id = cmd_buff[1]; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 1; // Set to registered + LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); +} + void InquireNotification(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[2]; - cmd_buff[1] = 0; // No error + u32 app_id = cmd_buff[1]; + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); } /** + * APT_U::SendParameter service function. This sets the parameter data state. + * Inputs: + * 1 : Source AppID + * 2 : Destination AppID + * 3 : Signal type + * 4 : Parameter buffer size, max size is 0x1000 (this can be zero) + * 5 : Value + * 6 : Handle to the destination process, likely used for shared memory (this can be zero) + * 7 : (Size<<14) | 2 + * 8 : Input parameter buffer ptr + * Outputs: + * 0 : Return Header + * 1 : Result of function, 0 on success, otherwise error code +*/ +void SendParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 src_app_id = cmd_buff[1]; + u32 dst_app_id = cmd_buff[2]; + u32 signal_type = cmd_buff[3]; + u32 buffer_size = cmd_buff[4]; + u32 value = cmd_buff[5]; + u32 handle = cmd_buff[6]; + u32 size = cmd_buff[7]; + u32 in_param_buffer_ptr = cmd_buff[8]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_APT, "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," + "buffer_size=0x%08X, value=0x%08X, handle=0x%08X, size=0x%08X, in_param_buffer_ptr=0x%08X", + src_app_id, dst_app_id, signal_type, buffer_size, value, handle, size, in_param_buffer_ptr); +} + +/** * APT_U::ReceiveParameter service function. This returns the current parameter data from NS state, * from the source process which set the parameters. Once finished, NS will clear a flag in the NS * state so that this command will return an error if this command is used again if parameters were @@ -101,8 +220,8 @@ void InquireNotification(Service::Interface* self) { * 2 : Parameter buffer size, max size is 0x1000 * Outputs: * 1 : Result of function, 0 on success, otherwise error code - * 2 : Unknown, for now assume AppID of the process which sent these parameters - * 3 : Unknown, for now assume Signal type + * 2 : AppID of the process which sent these parameters + * 3 : Signal type * 4 : Actual parameter buffer size, this is <= to the the input size * 5 : Value * 6 : Handle from the source process which set the parameters, likely used for shared memory @@ -113,7 +232,7 @@ void ReceiveParameter(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type cmd_buff[4] = 0x10; // Parameter buffer size (16) @@ -145,7 +264,7 @@ void GlanceParameter(Service::Interface* self) { u32 app_id = cmd_buff[1]; u32 buffer_size = cmd_buff[2]; - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = 0; cmd_buff[3] = static_cast<u32>(SignalType::AppJustStarted); // Signal type cmd_buff[4] = 0x10; // Parameter buffer size (16) @@ -157,6 +276,36 @@ void GlanceParameter(Service::Interface* self) { } /** + * APT_U::CancelParameter service function. When the parameter data is available, and when the above + * specified fields match the ones in NS state(for the ones where the checks are enabled), this + * clears the flag which indicates that parameter data is available + * (same flag cleared by APT:ReceiveParameter). + * Inputs: + * 1 : Flag, when non-zero NS will compare the word after this one with a field in the NS state. + * 2 : Unknown, this is the same as the first unknown field returned by APT:ReceiveParameter. + * 3 : Flag, when non-zero NS will compare the word after this one with a field in the NS state. + * 4 : AppID + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Status flag, 0 = failure due to no parameter data being available, or the above enabled + * fields don't match the fields in NS state. 1 = success. + */ +void CancelParameter(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 flag1 = cmd_buff[1]; + u32 unk = cmd_buff[2]; + u32 flag2 = cmd_buff[3]; + u32 app_id = cmd_buff[4]; + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 1; // Set to Success + + LOG_WARNING(Service_APT, "(STUBBED) called flag1=0x%08X, unk=0x%08X, flag2=0x%08X, app_id=0x%08X", + flag1, unk, flag2, app_id); +} + +/** * APT_U::AppletUtility service function * Inputs: * 1 : Unknown, but clearly used for something @@ -177,7 +326,7 @@ void AppletUtility(Service::Interface* self) { u32 buffer1_addr = cmd_buff[5]; u32 buffer2_addr = cmd_buff[65]; - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X, buffer1_size=0x%08x, buffer2_size=0x%08x, " "buffer1_addr=0x%08x, buffer2_addr=0x%08x", unk, buffer1_size, buffer2_size, @@ -201,96 +350,144 @@ void GetSharedFont(Service::Interface* self) { memcpy(Memory::GetPointer(SHARED_FONT_VADDR), shared_font.data(), shared_font.size()); cmd_buff[0] = 0x00440082; - cmd_buff[1] = 0; // No error + cmd_buff[1] = RESULT_SUCCESS.raw; // No error cmd_buff[2] = SHARED_FONT_VADDR; - cmd_buff[4] = shared_font_mem; + cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); } else { cmd_buff[1] = -1; // Generic error (not really possible to verify this on hardware) LOG_ERROR(Kernel_SVC, "called, but %s has not been loaded!", SHARED_FONT); } } +/** + * APT_U::SetAppCpuTimeLimit service function + * Inputs: + * 1 : Value, must be one + * 2 : Percentage of CPU time from 5 to 80 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +void SetAppCpuTimeLimit(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 value = cmd_buff[1]; + u32 percent = cmd_buff[2]; + + if (value != 1) { + LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); + } + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_WARNING(Service_APT, "(STUBBED) called percent=0x%08X, value=0x%08x", percent, value); +} + +/** + * APT_U::GetAppCpuTimeLimit service function + * Inputs: + * 1 : Value, must be one + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : System core CPU time percentage + */ +void GetAppCpuTimeLimit(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 value = cmd_buff[1]; + + if (value != 1) { + LOG_ERROR(Service_APT, "This value should be one, but is actually %u!", value); + } + + // TODO(purpasmart96): This is incorrect, I'm pretty sure the percentage should + // be set by the application. + + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[2] = 0x80; // Set to 80% + + LOG_WARNING(Service_APT, "(STUBBED) called value=0x%08x", value); +} + const Interface::FunctionInfo FunctionTable[] = { - {0x00010040, GetLockHandle, "GetLockHandle"}, - {0x00020080, Initialize, "Initialize"}, - {0x00030040, Enable, "Enable"}, - {0x00040040, nullptr, "Finalize"}, - {0x00050040, nullptr, "GetAppletManInfo"}, - {0x00060040, nullptr, "GetAppletInfo"}, - {0x00070000, nullptr, "GetLastSignaledAppletId"}, - {0x00080000, nullptr, "CountRegisteredApplet"}, - {0x00090040, nullptr, "IsRegistered"}, - {0x000A0040, nullptr, "GetAttribute"}, - {0x000B0040, InquireNotification, "InquireNotification"}, - {0x000C0104, nullptr, "SendParameter"}, - {0x000D0080, ReceiveParameter, "ReceiveParameter"}, - {0x000E0080, GlanceParameter, "GlanceParameter"}, - {0x000F0100, nullptr, "CancelParameter"}, - {0x001000C2, nullptr, "DebugFunc"}, - {0x001100C0, nullptr, "MapProgramIdForDebug"}, - {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, - {0x00130000, nullptr, "GetPreparationState"}, - {0x00140040, nullptr, "SetPreparationState"}, - {0x00150140, nullptr, "PrepareToStartApplication"}, - {0x00160040, nullptr, "PreloadLibraryApplet"}, - {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, - {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, - {0x00190040, nullptr, "PrepareToStartSystemApplet"}, - {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, - {0x001B00C4, nullptr, "StartApplication"}, - {0x001C0000, nullptr, "WakeupApplication"}, - {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, - {0x001F0084, nullptr, "StartSystemApplet"}, - {0x00200044, nullptr, "StartNewestHomeMenu"}, - {0x00210000, nullptr, "OrderToCloseApplication"}, - {0x00220040, nullptr, "PrepareToCloseApplication"}, - {0x00230040, nullptr, "PrepareToJumpToApplication"}, - {0x00240044, nullptr, "JumpToApplication"}, - {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, - {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, - {0x00270044, nullptr, "CloseApplication"}, - {0x00280044, nullptr, "CloseLibraryApplet"}, - {0x00290044, nullptr, "CloseSystemApplet"}, - {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, - {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, - {0x002C0044, nullptr, "JumpToHomeMenu"}, - {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, - {0x002E0044, nullptr, "LeaveHomeMenu"}, - {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, - {0x00300044, nullptr, "LeaveResidentApplet"}, - {0x00310100, nullptr, "PrepareToDoApplicationJump"}, - {0x00320084, nullptr, "DoApplicationJump"}, - {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, - {0x00340084, nullptr, "SendDeliverArg"}, - {0x00350080, nullptr, "ReceiveDeliverArg"}, - {0x00360040, nullptr, "LoadSysMenuArg"}, - {0x00370042, nullptr, "StoreSysMenuArg"}, - {0x00380040, nullptr, "PreloadResidentApplet"}, - {0x00390040, nullptr, "PrepareToStartResidentApplet"}, - {0x003A0044, nullptr, "StartResidentApplet"}, - {0x003B0040, nullptr, "CancelLibraryApplet"}, - {0x003C0042, nullptr, "SendDspSleep"}, - {0x003D0042, nullptr, "SendDspWakeUp"}, - {0x003E0080, nullptr, "ReplySleepQuery"}, - {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, - {0x00400042, nullptr, "SendCaptureBufferInfo"}, - {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, - {0x00420080, nullptr, "SleepSystem"}, - {0x00430040, nullptr, "NotifyToWait"}, - {0x00440000, GetSharedFont, "GetSharedFont"}, - {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, - {0x00480100, nullptr, "GetProgramInfo"}, - {0x00490180, nullptr, "Reboot"}, - {0x004A0040, nullptr, "GetCaptureInfo"}, - {0x004B00C2, AppletUtility, "AppletUtility"}, - {0x004C0000, nullptr, "SetFatalErrDispMode"}, - {0x004D0080, nullptr, "GetAppletProgramInfo"}, - {0x004E0000, nullptr, "HardwareResetAsync"}, - {0x004F0080, nullptr, "SetApplicationCpuTimeLimit"}, - {0x00500040, nullptr, "GetApplicationCpuTimeLimit"}, + {0x00010040, GetLockHandle, "GetLockHandle"}, + {0x00020080, Initialize, "Initialize"}, + {0x00030040, Enable, "Enable"}, + {0x00040040, nullptr, "Finalize"}, + {0x00050040, GetAppletManInfo, "GetAppletManInfo"}, + {0x00060040, nullptr, "GetAppletInfo"}, + {0x00070000, nullptr, "GetLastSignaledAppletId"}, + {0x00080000, nullptr, "CountRegisteredApplet"}, + {0x00090040, IsRegistered, "IsRegistered"}, + {0x000A0040, nullptr, "GetAttribute"}, + {0x000B0040, InquireNotification, "InquireNotification"}, + {0x000C0104, SendParameter, "SendParameter"}, + {0x000D0080, ReceiveParameter, "ReceiveParameter"}, + {0x000E0080, GlanceParameter, "GlanceParameter"}, + {0x000F0100, CancelParameter, "CancelParameter"}, + {0x001000C2, nullptr, "DebugFunc"}, + {0x001100C0, nullptr, "MapProgramIdForDebug"}, + {0x00120040, nullptr, "SetHomeMenuAppletIdForDebug"}, + {0x00130000, nullptr, "GetPreparationState"}, + {0x00140040, nullptr, "SetPreparationState"}, + {0x00150140, nullptr, "PrepareToStartApplication"}, + {0x00160040, nullptr, "PreloadLibraryApplet"}, + {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, + {0x00180040, nullptr, "PrepareToStartLibraryApplet"}, + {0x00190040, nullptr, "PrepareToStartSystemApplet"}, + {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001B00C4, nullptr, "StartApplication"}, + {0x001C0000, nullptr, "WakeupApplication"}, + {0x001D0000, nullptr, "CancelApplication"}, + {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001F0084, nullptr, "StartSystemApplet"}, + {0x00200044, nullptr, "StartNewestHomeMenu"}, + {0x00210000, nullptr, "OrderToCloseApplication"}, + {0x00220040, nullptr, "PrepareToCloseApplication"}, + {0x00230040, nullptr, "PrepareToJumpToApplication"}, + {0x00240044, nullptr, "JumpToApplication"}, + {0x002500C0, nullptr, "PrepareToCloseLibraryApplet"}, + {0x00260000, nullptr, "PrepareToCloseSystemApplet"}, + {0x00270044, nullptr, "CloseApplication"}, + {0x00280044, nullptr, "CloseLibraryApplet"}, + {0x00290044, nullptr, "CloseSystemApplet"}, + {0x002A0000, nullptr, "OrderToCloseSystemApplet"}, + {0x002B0000, nullptr, "PrepareToJumpToHomeMenu"}, + {0x002C0044, nullptr, "JumpToHomeMenu"}, + {0x002D0000, nullptr, "PrepareToLeaveHomeMenu"}, + {0x002E0044, nullptr, "LeaveHomeMenu"}, + {0x002F0040, nullptr, "PrepareToLeaveResidentApplet"}, + {0x00300044, nullptr, "LeaveResidentApplet"}, + {0x00310100, nullptr, "PrepareToDoApplicationJump"}, + {0x00320084, nullptr, "DoApplicationJump"}, + {0x00330000, nullptr, "GetProgramIdOnApplicationJump"}, + {0x00340084, nullptr, "SendDeliverArg"}, + {0x00350080, nullptr, "ReceiveDeliverArg"}, + {0x00360040, nullptr, "LoadSysMenuArg"}, + {0x00370042, nullptr, "StoreSysMenuArg"}, + {0x00380040, nullptr, "PreloadResidentApplet"}, + {0x00390040, nullptr, "PrepareToStartResidentApplet"}, + {0x003A0044, nullptr, "StartResidentApplet"}, + {0x003B0040, nullptr, "CancelLibraryApplet"}, + {0x003C0042, nullptr, "SendDspSleep"}, + {0x003D0042, nullptr, "SendDspWakeUp"}, + {0x003E0080, nullptr, "ReplySleepQuery"}, + {0x003F0040, nullptr, "ReplySleepNotificationComplete"}, + {0x00400042, nullptr, "SendCaptureBufferInfo"}, + {0x00410040, nullptr, "ReceiveCaptureBufferInfo"}, + {0x00420080, nullptr, "SleepSystem"}, + {0x00430040, NotifyToWait, "NotifyToWait"}, + {0x00440000, GetSharedFont, "GetSharedFont"}, + {0x00450040, nullptr, "GetWirelessRebootInfo"}, + {0x00460104, nullptr, "Wrap"}, + {0x00470104, nullptr, "Unwrap"}, + {0x00480100, nullptr, "GetProgramInfo"}, + {0x00490180, nullptr, "Reboot"}, + {0x004A0040, nullptr, "GetCaptureInfo"}, + {0x004B00C2, AppletUtility, "AppletUtility"}, + {0x004C0000, nullptr, "SetFatalErrDispMode"}, + {0x004D0080, nullptr, "GetAppletProgramInfo"}, + {0x004E0000, nullptr, "HardwareResetAsync"}, + {0x004F0080, SetAppCpuTimeLimit, "SetAppCpuTimeLimit"}, + {0x00500040, GetAppCpuTimeLimit, "GetAppCpuTimeLimit"}, }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -315,15 +512,15 @@ Interface::Interface() { file.ReadBytes(shared_font.data(), (size_t)file.GetSize()); // Create shared font memory object - shared_font_mem = Kernel::CreateSharedMemory("APT_U:shared_font_mem"); + shared_font_mem = Kernel::SharedMemory::Create("APT_U:shared_font_mem"); } else { LOG_WARNING(Service_APT, "Unable to load shared font: %s", filepath.c_str()); - shared_font_mem = 0; + shared_font_mem = nullptr; } - lock_handle = 0; + lock = Kernel::Mutex::Create(false, "APT_U:Lock"); - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/boss_p.cpp b/src/core/hle/service/boss_p.cpp new file mode 100644 index 000000000..b3aa6acee --- /dev/null +++ b/src/core/hle/service/boss_p.cpp @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/boss_p.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace BOSS_P + +namespace BOSS_P { + +// Empty arrays are illegal -- commented out until an entry is added. +// const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/boss_p.h b/src/core/hle/service/boss_p.h new file mode 100644 index 000000000..71f1e7464 --- /dev/null +++ b/src/core/hle/service/boss_p.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace BOSS_P + +namespace BOSS_P { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "boss:P"; + } +}; + +} // namespace diff --git a/src/core/hle/service/boss_u.cpp b/src/core/hle/service/boss_u.cpp index 1820ea7ad..50bb5d426 100644 --- a/src/core/hle/service/boss_u.cpp +++ b/src/core/hle/service/boss_u.cpp @@ -19,7 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/cam_u.cpp b/src/core/hle/service/cam_u.cpp new file mode 100644 index 000000000..cf3b27664 --- /dev/null +++ b/src/core/hle/service/cam_u.cpp @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/cam_u.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CAM_U + +namespace CAM_U { + +// Empty arrays are illegal -- commented out until an entry is added. +//const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/cam_u.h b/src/core/hle/service/cam_u.h new file mode 100644 index 000000000..878c20a84 --- /dev/null +++ b/src/core/hle/service/cam_u.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CAM_U + +namespace CAM_U { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "cam:u"; + } +}; + +} // namespace diff --git a/src/core/hle/service/cecd_s.cpp b/src/core/hle/service/cecd_s.cpp new file mode 100644 index 000000000..2c707baff --- /dev/null +++ b/src/core/hle/service/cecd_s.cpp @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/cecd_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CECD_S + +namespace CECD_S { + +// Empty arrays are illegal -- commented out until an entry is added. +//const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/cecd_s.h b/src/core/hle/service/cecd_s.h new file mode 100644 index 000000000..d880d0391 --- /dev/null +++ b/src/core/hle/service/cecd_s.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CECD_S + +namespace CECD_S { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "cecd:s"; + } +}; + +} // namespace diff --git a/src/core/hle/service/cecd_u.cpp b/src/core/hle/service/cecd_u.cpp index b7655ef0b..b7ea3a186 100644 --- a/src/core/hle/service/cecd_u.cpp +++ b/src/core/hle/service/cecd_u.cpp @@ -18,7 +18,7 @@ namespace CECD_U { // Interface class Interface::Interface() { - //Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + //Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index c74527ca4..e818d7bdc 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -110,7 +110,7 @@ ResultCode GetConfigInfoBlock(u32 block_id, u32 size, u32 flag, u8* output); * The config savegame file in the filesystem is not updated. * @param block_id The id of the block we want to create * @param size The size of the block we want to create - * @param flag The flags of the new block + * @param flags The flags of the new block * @param data A pointer containing the data we will write to the new block * @returns ResultCode indicating the result of the operation, 0 on success */ diff --git a/src/core/hle/service/cfg/cfg_i.cpp b/src/core/hle/service/cfg/cfg_i.cpp index 7c1ee8ac3..555b7884a 100644 --- a/src/core/hle/service/cfg/cfg_i.cpp +++ b/src/core/hle/service/cfg/cfg_i.cpp @@ -104,7 +104,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/cfg/cfg_s.cpp b/src/core/hle/service/cfg/cfg_s.cpp new file mode 100644 index 000000000..2170894d6 --- /dev/null +++ b/src/core/hle/service/cfg/cfg_s.cpp @@ -0,0 +1,98 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/cfg/cfg.h" +#include "core/hle/service/cfg/cfg_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CFG_S + +namespace CFG_S { + +/** + * CFG_S::GetConfigInfoBlk2 service function + * Inputs: + * 0 : 0x00010082 + * 1 : Size + * 2 : Block ID + * 3 : Descriptor for the output buffer + * 4 : Output buffer pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetConfigInfoBlk2(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + u32 size = cmd_buffer[1]; + u32 block_id = cmd_buffer[2]; + u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); + + if (data_pointer == nullptr) { + cmd_buffer[1] = -1; // TODO(Subv): Find the right error code + return; + } + + cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x2, data_pointer).raw; +} + +/** + * CFG_S::GetConfigInfoBlk8 service function + * Inputs: + * 0 : 0x04010082 + * 1 : Size + * 2 : Block ID + * 3 : Descriptor for the output buffer + * 4 : Output buffer pointer + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void GetConfigInfoBlk8(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + u32 size = cmd_buffer[1]; + u32 block_id = cmd_buffer[2]; + u8* data_pointer = Memory::GetPointer(cmd_buffer[4]); + + if (data_pointer == nullptr) { + cmd_buffer[1] = -1; // TODO(Subv): Find the right error code + return; + } + + cmd_buffer[1] = Service::CFG::GetConfigInfoBlock(block_id, size, 0x8, data_pointer).raw; +} + +/** + * CFG_S::UpdateConfigNANDSavegame service function + * Inputs: + * 0 : 0x04030000 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void UpdateConfigNANDSavegame(Service::Interface* self) { + u32* cmd_buffer = Kernel::GetCommandBuffer(); + cmd_buffer[1] = Service::CFG::UpdateConfigNANDSavegame().raw; +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x00010082, GetConfigInfoBlk2, "GetConfigInfoBlk2"}, + {0x00020000, nullptr, "SecureInfoGetRegion"}, + {0x04010082, GetConfigInfoBlk8, "GetConfigInfoBlk8"}, + {0x04020082, nullptr, "SetConfigInfoBlk4"}, + {0x04030000, UpdateConfigNANDSavegame, "UpdateConfigNANDSavegame"}, + {0x04040042, nullptr, "GetLocalFriendCodeSeedData"}, + {0x04050000, nullptr, "GetLocalFriendCodeSeed"}, + {0x04060000, nullptr, "SecureInfoGetRegion"}, + {0x04070000, nullptr, "SecureInfoGetByte101"}, + {0x04080042, nullptr, "SecureInfoGetSerialNo"}, + {0x04090000, nullptr, "UpdateConfigBlk00040003"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/cfg/cfg_s.h b/src/core/hle/service/cfg/cfg_s.h new file mode 100644 index 000000000..d8b67137f --- /dev/null +++ b/src/core/hle/service/cfg/cfg_s.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace CFG_S + +namespace CFG_S { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "cfg:s"; + } +}; + +} // namespace diff --git a/src/core/hle/service/cfg/cfg_u.cpp b/src/core/hle/service/cfg/cfg_u.cpp index 835620909..1da9f59f6 100644 --- a/src/core/hle/service/cfg/cfg_u.cpp +++ b/src/core/hle/service/cfg/cfg_u.cpp @@ -186,7 +186,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 3a557efe1..39b00982c 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -29,7 +29,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index d4affdfbf..0f86894a6 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -13,8 +13,8 @@ namespace DSP_DSP { static u32 read_pipe_count = 0; -static Handle semaphore_event = 0; -static Handle interrupt_event = 0; +static Kernel::SharedPtr<Kernel::Event> semaphore_event; +static Kernel::SharedPtr<Kernel::Event> interrupt_event; void SignalInterrupt() { // TODO(bunnei): This is just a stub, it does not do anything other than signal to the emulated @@ -23,11 +23,8 @@ void SignalInterrupt() { // that check the DSP interrupt signal event to run. We should figure out the different types of // DSP interrupts, and trigger them at the appropriate times. - if (interrupt_event == 0) { - LOG_WARNING(Service_DSP, "cannot signal interrupt until DSP event has been created!"); - return; - } - Kernel::SignalEvent(interrupt_event); + if (interrupt_event != 0) + interrupt_event->Signal(); } /** @@ -81,8 +78,8 @@ void LoadComponent(Service::Interface* self) { void GetSemaphoreEventHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = 0; // No error - cmd_buff[3] = semaphore_event; // Event handle + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle LOG_WARNING(Service_DSP, "(STUBBED) called"); } @@ -99,9 +96,16 @@ void GetSemaphoreEventHandle(Service::Interface* self) { void RegisterInterruptEvents(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - interrupt_event = static_cast<Handle>(cmd_buff[4]); + auto evt = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[4]); + if (evt != nullptr) { + interrupt_event = evt; + cmd_buff[1] = 0; // No error + } else { + LOG_ERROR(Service_DSP, "called with invalid handle=%08X", cmd_buff[4]); - cmd_buff[1] = 0; // No error + // TODO(yuriks): An error should be returned from SendSyncRequest, not in the cmdbuf + cmd_buff[1] = -1; + } LOG_WARNING(Service_DSP, "(STUBBED) called"); } @@ -197,11 +201,11 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - semaphore_event = Kernel::CreateEvent(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event"); - interrupt_event = 0; + semaphore_event = Kernel::Event::Create(RESETTYPE_ONESHOT, "DSP_DSP::semaphore_event"); + interrupt_event = nullptr; read_pipe_count = 0; - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/err_f.cpp b/src/core/hle/service/err_f.cpp index 8c900eabc..962de2170 100644 --- a/src/core/hle/service/err_f.cpp +++ b/src/core/hle/service/err_f.cpp @@ -19,7 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/frd_a.cpp b/src/core/hle/service/frd_a.cpp new file mode 100644 index 000000000..79140a756 --- /dev/null +++ b/src/core/hle/service/frd_a.cpp @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/frd_a.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace FRD_A + +namespace FRD_A { + +// Empty arrays are illegal -- commented out until an entry is added. +// const Interface::FunctionInfo FunctionTable[] = { }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/frd_a.h b/src/core/hle/service/frd_a.h new file mode 100644 index 000000000..f068c6108 --- /dev/null +++ b/src/core/hle/service/frd_a.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace FRD_A + +namespace FRD_A { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "frd:a"; + } +}; + +} // namespace diff --git a/src/core/hle/service/frd_u.cpp b/src/core/hle/service/frd_u.cpp index 021186e57..59faca77a 100644 --- a/src/core/hle/service/frd_u.cpp +++ b/src/core/hle/service/frd_u.cpp @@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 958dd9344..ccf132f31 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -43,6 +43,11 @@ const std::string SDCARD_ID = "00000000000000000000000000000000"; namespace Service { namespace FS { +// TODO: Verify code +/// Returned when a function is passed an invalid handle. +const ResultCode ERR_INVALID_HANDLE(ErrorDescription::InvalidHandle, ErrorModule::FS, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + // Command to access archive file enum class FileCommand : u32 { Dummy1 = 0x000100C6, @@ -84,7 +89,7 @@ public: class File : public Kernel::Session { public: File(std::unique_ptr<FileSys::FileBackend>&& backend, const FileSys::Path& path) - : path(path), backend(std::move(backend)), priority(0) { + : path(path), priority(0), backend(std::move(backend)) { } std::string GetName() const override { return "Path: " + path.DebugStr(); } @@ -158,7 +163,7 @@ public: case FileCommand::OpenLinkFile: { LOG_WARNING(Service_FS, "(STUBBED) File command OpenLinkFile %s", GetName().c_str()); - cmd_buff[3] = GetHandle(); + cmd_buff[3] = Kernel::g_handle_table.Create(this).ValueOr(INVALID_HANDLE); break; } @@ -280,7 +285,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi ResultCode CloseArchive(ArchiveHandle handle) { if (handle_map.erase(handle) == 0) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; else return RESULT_SUCCESS; } @@ -298,10 +303,11 @@ ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, Arc return RESULT_SUCCESS; } -ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode) { +ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path, const FileSys::Mode mode) { Archive* archive = GetArchive(archive_handle); if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; std::unique_ptr<FileSys::FileBackend> backend = archive->backend->OpenFile(path, mode); if (backend == nullptr) { @@ -309,16 +315,14 @@ ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSy ErrorSummary::NotFound, ErrorLevel::Status); } - auto file = Common::make_unique<File>(std::move(backend), path); - // TOOD(yuriks): Fix error reporting - Handle handle = Kernel::g_handle_table.Create(file.release()).ValueOr(INVALID_HANDLE); - return MakeResult<Handle>(handle); + auto file = Kernel::SharedPtr<File>(new File(std::move(backend), path)); + return MakeResult<Kernel::SharedPtr<Kernel::Session>>(std::move(file)); } ResultCode DeleteFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { Archive* archive = GetArchive(archive_handle); if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; if (archive->backend->DeleteFile(path)) return RESULT_SUCCESS; @@ -331,7 +335,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil Archive* src_archive = GetArchive(src_archive_handle); Archive* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; if (src_archive == dest_archive) { if (src_archive->backend->RenameFile(src_path, dest_path)) @@ -350,7 +354,7 @@ ResultCode RenameFileBetweenArchives(ArchiveHandle src_archive_handle, const Fil ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { Archive* archive = GetArchive(archive_handle); if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; if (archive->backend->DeleteDirectory(path)) return RESULT_SUCCESS; @@ -361,7 +365,7 @@ ResultCode DeleteDirectoryFromArchive(ArchiveHandle archive_handle, const FileSy ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path& path, u32 file_size) { Archive* archive = GetArchive(archive_handle); if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; return archive->backend->CreateFile(path, file_size); } @@ -369,7 +373,7 @@ ResultCode CreateFileInArchive(ArchiveHandle archive_handle, const FileSys::Path ResultCode CreateDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { Archive* archive = GetArchive(archive_handle); if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; if (archive->backend->CreateDirectory(path)) return RESULT_SUCCESS; @@ -382,7 +386,7 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons Archive* src_archive = GetArchive(src_archive_handle); Archive* dest_archive = GetArchive(dest_archive_handle); if (src_archive == nullptr || dest_archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; if (src_archive == dest_archive) { if (src_archive->backend->RenameDirectory(src_path, dest_path)) @@ -398,16 +402,11 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons ErrorSummary::NothingHappened, ErrorLevel::Status); } -/** - * Open a Directory from an Archive - * @param archive_handle Handle to an open Archive object - * @param path Path to the Directory inside of the Archive - * @return Opened Directory object - */ -ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path) { +ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path) { Archive* archive = GetArchive(archive_handle); if (archive == nullptr) - return InvalidHandle(ErrorModule::FS); + return ERR_INVALID_HANDLE; std::unique_ptr<FileSys::DirectoryBackend> backend = archive->backend->OpenDirectory(path); if (backend == nullptr) { @@ -415,10 +414,8 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F ErrorSummary::NotFound, ErrorLevel::Permanent); } - auto directory = Common::make_unique<Directory>(std::move(backend), path); - // TOOD(yuriks): Fix error reporting - Handle handle = Kernel::g_handle_table.Create(directory.release()).ValueOr(INVALID_HANDLE); - return MakeResult<Handle>(handle); + auto directory = Kernel::SharedPtr<Directory>(new Directory(std::move(backend), path)); + return MakeResult<Kernel::SharedPtr<Kernel::Session>>(std::move(directory)); } ResultCode FormatSaveData() { @@ -432,6 +429,28 @@ ResultCode FormatSaveData() { return archive_itr->second->backend->Format(FileSys::Path()); } +ResultCode CreateExtSaveData(u32 high, u32 low) { + // Construct the binary path to the archive first + std::vector<u8> binary_path; + binary_path.reserve(12); + // The first word is all zero to specify a NAND archive + for (unsigned i = 0; i < 4; ++i) + binary_path.push_back(0); + // Next is the low word + for (unsigned i = 0; i < 4; ++i) + binary_path.push_back((low >> (8 * i)) & 0xFF); + // Next is the high word + for (unsigned i = 0; i < 4; ++i) + binary_path.push_back((high >> i) & 0xFF); + FileSys::Path path(binary_path); + std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); + std::string base_path = FileSys::GetExtDataContainerPath(nand_directory, true); + std::string extsavedata_path = FileSys::GetExtSaveDataPath(base_path, path); + if (!FileUtil::CreateFullPath(extsavedata_path)) + return ResultCode(-1); // TODO(Subv): Find the right error code + return RESULT_SUCCESS; +} + /// Initialize archives void ArchiveInit() { next_handle = 1; diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index b3f2134f2..ab5ea4da8 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -15,6 +15,10 @@ extern const std::string SYSTEM_ID; /// The scrambled SD card CID, also known as ID1 extern const std::string SDCARD_ID; +namespace Kernel { + class Session; +} + namespace Service { namespace FS { @@ -42,7 +46,7 @@ ResultVal<ArchiveHandle> OpenArchive(ArchiveIdCode id_code, FileSys::Path& archi /** * Closes an archive - * @param id_code IdCode of the archive to open + * @param handle Handle to the archive to close */ ResultCode CloseArchive(ArchiveHandle handle); @@ -58,9 +62,10 @@ ResultCode CreateArchive(std::unique_ptr<FileSys::ArchiveBackend>&& backend, Arc * @param archive_handle Handle to an open Archive object * @param path Path to the File inside of the Archive * @param mode Mode under which to open the File - * @return Handle to the opened File object + * @return The opened File object as a Session */ -ResultVal<Handle> OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); +ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenFileFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path, const FileSys::Mode mode); /** * Delete a File from an Archive @@ -121,9 +126,10 @@ ResultCode RenameDirectoryBetweenArchives(ArchiveHandle src_archive_handle, cons * Open a Directory from an Archive * @param archive_handle Handle to an open Archive object * @param path Path to the Directory inside of the Archive - * @return Handle to the opened File object + * @return The opened Directory object as a Session */ -ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path); +ResultVal<Kernel::SharedPtr<Kernel::Session>> OpenDirectoryFromArchive(ArchiveHandle archive_handle, + const FileSys::Path& path); /** * Creates a blank SaveData archive. @@ -131,6 +137,14 @@ ResultVal<Handle> OpenDirectoryFromArchive(ArchiveHandle archive_handle, const F */ ResultCode FormatSaveData(); +/** + * Creates a blank SharedExtSaveData archive for the specified extdata ID + * @param high The high word of the extdata id to create + * @param low The low word of the extdata id to create + * @return ResultCode 0 on success or the corresponding code on error + */ +ResultCode CreateExtSaveData(u32 high, u32 low); + /// Initialize archives void ArchiveInit(); diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 56f3117f4..94a3a31c8 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -14,6 +14,9 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace FS_User +using Kernel::SharedPtr; +using Kernel::Session; + namespace Service { namespace FS { @@ -58,10 +61,10 @@ static void OpenFile(Service::Interface* self) { LOG_DEBUG(Service_FS, "path=%s, mode=%d attrs=%u", file_path.DebugStr().c_str(), mode.hex, attributes); - ResultVal<Handle> handle = OpenFileFromArchive(archive_handle, file_path, mode); - cmd_buff[1] = handle.Code().raw; - if (handle.Succeeded()) { - cmd_buff[3] = *handle; + ResultVal<SharedPtr<Session>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); + cmd_buff[1] = file_res.Code().raw; + if (file_res.Succeeded()) { + cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); @@ -114,10 +117,10 @@ static void OpenFileDirectly(Service::Interface* self) { } SCOPE_EXIT({ CloseArchive(*archive_handle); }); - ResultVal<Handle> handle = OpenFileFromArchive(*archive_handle, file_path, mode); - cmd_buff[1] = handle.Code().raw; - if (handle.Succeeded()) { - cmd_buff[3] = *handle; + ResultVal<SharedPtr<Session>> file_res = OpenFileFromArchive(*archive_handle, file_path, mode); + cmd_buff[1] = file_res.Code().raw; + if (file_res.Succeeded()) { + cmd_buff[3] = Kernel::g_handle_table.Create(*file_res).MoveFrom(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); @@ -334,10 +337,10 @@ static void OpenDirectory(Service::Interface* self) { LOG_DEBUG(Service_FS, "type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); - ResultVal<Handle> handle = OpenDirectoryFromArchive(archive_handle, dir_path); - cmd_buff[1] = handle.Code().raw; - if (handle.Succeeded()) { - cmd_buff[3] = *handle; + ResultVal<SharedPtr<Session>> dir_res = OpenDirectoryFromArchive(archive_handle, dir_path); + cmd_buff[1] = dir_res.Code().raw; + if (dir_res.Succeeded()) { + cmd_buff[3] = Kernel::g_handle_table.Create(*dir_res).MoveFrom(); } else { LOG_ERROR(Service_FS, "failed to get a handle for directory"); } @@ -484,6 +487,15 @@ static void FormatThisUserSaveData(Service::Interface* self) { cmd_buff[1] = FormatSaveData().raw; } +static void CreateExtSaveData(Service::Interface* self) { + // TODO(Subv): Figure out the other parameters. + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 save_high = cmd_buff[1]; + u32 save_low = cmd_buff[2]; + // TODO(Subv): For now it is assumed that only SharedExtSaveData can be created like this + cmd_buff[1] = CreateExtSaveData(save_high, save_low).raw; +} + const FSUserInterface::FunctionInfo FunctionTable[] = { {0x000100C6, nullptr, "Dummy1"}, {0x040100C4, nullptr, "Control"}, @@ -567,6 +579,8 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { {0x084E0342, nullptr, "UpdateSha256Context"}, {0x084F0102, nullptr, "ReadSpecialFile"}, {0x08500040, nullptr, "GetSpecialFileSize"}, + {0x08510242, CreateExtSaveData, "CreateExtSaveData"}, + {0x08520100, nullptr, "DeleteExtSaveData"}, {0x08580000, nullptr, "GetMovableSedHashedKeyYRandomData"}, {0x08610042, nullptr, "InitializeWithSdkVersion"}, {0x08620040, nullptr, "SetPriority"}, @@ -577,7 +591,7 @@ const FSUserInterface::FunctionInfo FunctionTable[] = { // Interface class FSUserInterface::FSUserInterface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace FS diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 2b115240f..495c117ee 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -22,13 +22,16 @@ GraphicsDebugger g_debugger; namespace GSP_GPU { -Handle g_interrupt_event = 0; ///< Handle to event triggered when GSP interrupt has been signalled -Handle g_shared_memory = 0; ///< Handle to GSP shared memorys -u32 g_thread_id = 1; ///< Thread index into interrupt relay queue, 1 is arbitrary +/// Event triggered when GSP interrupt has been signalled +Kernel::SharedPtr<Kernel::Event> g_interrupt_event; +/// GSP shared memoryings +Kernel::SharedPtr<Kernel::SharedMemory> g_shared_memory; +/// Thread index into interrupt relay queue, 1 is arbitrary +u32 g_thread_id = 1; /// Gets a pointer to a thread command buffer in GSP shared memory static inline u8* GetCommandBuffer(u32 thread_id) { - ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, 0x800 + (thread_id * sizeof(CommandBuffer))); + ResultVal<u8*> ptr = g_shared_memory->GetPointer(0x800 + (thread_id * sizeof(CommandBuffer))); return ptr.ValueOr(nullptr); } @@ -37,13 +40,13 @@ static inline FrameBufferUpdate* GetFrameBufferInfo(u32 thread_id, u32 screen_in // For each thread there are two FrameBufferUpdate fields u32 offset = 0x200 + (2 * thread_id + screen_index) * sizeof(FrameBufferUpdate); - ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, offset); + ResultVal<u8*> ptr = g_shared_memory->GetPointer(offset); return reinterpret_cast<FrameBufferUpdate*>(ptr.ValueOr(nullptr)); } /// Gets a pointer to the interrupt relay queue for a given thread index static inline InterruptRelayQueue* GetInterruptRelayQueue(u32 thread_id) { - ResultVal<u8*> ptr = Kernel::GetSharedMemoryPointer(g_shared_memory, sizeof(InterruptRelayQueue) * thread_id); + ResultVal<u8*> ptr = g_shared_memory->GetPointer(sizeof(InterruptRelayQueue) * thread_id); return reinterpret_cast<InterruptRelayQueue*>(ptr.ValueOr(nullptr)); } @@ -181,16 +184,18 @@ static void FlushDataCache(Service::Interface* self) { static void RegisterInterruptRelayQueue(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); u32 flags = cmd_buff[1]; - g_interrupt_event = cmd_buff[3]; - g_shared_memory = Kernel::CreateSharedMemory("GSPSharedMem"); - _assert_msg_(GSP, (g_interrupt_event != 0), "handle is not valid!"); + g_interrupt_event = Kernel::g_handle_table.Get<Kernel::Event>(cmd_buff[3]); + _assert_msg_(GSP, (g_interrupt_event != nullptr), "handle is not valid!"); + g_shared_memory = Kernel::SharedMemory::Create("GSPSharedMem"); + + Handle shmem_handle = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); cmd_buff[1] = 0x2A07; // Value verified by 3dmoo team, purpose unknown, but needed for GSP init cmd_buff[2] = g_thread_id++; // Thread ID - cmd_buff[4] = g_shared_memory; // GSP shared memory + cmd_buff[4] = shmem_handle; // GSP shared memory - Kernel::SignalEvent(g_interrupt_event); // TODO(bunnei): Is this correct? + g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? } /** @@ -204,22 +209,35 @@ void SignalInterrupt(InterruptId interrupt_id) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP event has been created!"); return; } - if (0 == g_shared_memory) { + if (nullptr == g_shared_memory) { LOG_WARNING(Service_GSP, "cannot synchronize until GSP shared memory has been created!"); return; } for (int thread_id = 0; thread_id < 0x4; ++thread_id) { InterruptRelayQueue* interrupt_relay_queue = GetInterruptRelayQueue(thread_id); - interrupt_relay_queue->number_interrupts = interrupt_relay_queue->number_interrupts + 1; - u8 next = interrupt_relay_queue->index; next += interrupt_relay_queue->number_interrupts; next = next % 0x34; // 0x34 is the number of interrupt slots + interrupt_relay_queue->number_interrupts += 1; + interrupt_relay_queue->slot[next] = interrupt_id; interrupt_relay_queue->error_code = 0x0; // No error + + // Update framebuffer information if requested + // TODO(yuriks): Confirm where this code should be called. It is definitely updated without + // executing any GSP commands, only waiting on the event. + for (int screen_id = 0; screen_id < 2; ++screen_id) { + FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); + + if (info->is_dirty) { + SetBufferSwap(screen_id, info->framebuffer_info[info->index]); + } + + info->is_dirty = false; + } } - Kernel::SignalEvent(g_interrupt_event); + g_interrupt_event->Signal(); } /// Executes the next GSP command @@ -269,8 +287,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].address_end), Memory::VirtualToPhysicalAddress(params.end2) >> 3); WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].size), params.end2 - params.start2); WriteGPURegister(GPU_REG_INDEX(memory_fill_config[1].value), params.value2); - - SignalInterrupt(InterruptId::PSC0); break; } @@ -283,22 +299,6 @@ static void ExecuteCommand(const Command& command, u32 thread_id) { WriteGPURegister(GPU_REG_INDEX(display_transfer_config.output_size), params.out_buffer_size); WriteGPURegister(GPU_REG_INDEX(display_transfer_config.flags), params.flags); WriteGPURegister(GPU_REG_INDEX(display_transfer_config.trigger), 1); - - // TODO(bunnei): Determine if these interrupts should be signalled here. - SignalInterrupt(InterruptId::PSC1); - SignalInterrupt(InterruptId::PPF); - - // Update framebuffer information if requested - for (int screen_id = 0; screen_id < 2; ++screen_id) { - FrameBufferUpdate* info = GetFrameBufferInfo(thread_id, screen_id); - - if (info->is_dirty) { - SetBufferSwap(screen_id, info->framebuffer_info[info->index]); - info->framebuffer_info->active_fb = info->framebuffer_info->active_fb ^ 1; - } - - info->is_dirty = false; - } break; } @@ -389,7 +389,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); g_interrupt_event = 0; g_shared_memory = 0; diff --git a/src/core/hle/service/gsp_gpu.h b/src/core/hle/service/gsp_gpu.h index 932b6170f..65abb194a 100644 --- a/src/core/hle/service/gsp_gpu.h +++ b/src/core/hle/service/gsp_gpu.h @@ -45,21 +45,16 @@ enum class CommandId : u32 { /// GSP thread interrupt relay queue struct InterruptRelayQueue { - union { - u32 hex; - - // Index of last interrupt in the queue - BitField<0,8,u32> index; - - // Number of interrupts remaining to be processed by the userland code - BitField<8,8,u32> number_interrupts; - - // Error code - zero on success, otherwise an error has occurred - BitField<16,8,u32> error_code; - }; - - u32 unk0; - u32 unk1; + // Index of last interrupt in the queue + u8 index; + // Number of interrupts remaining to be processed by the userland code + u8 number_interrupts; + // Error code - zero on success, otherwise an error has occurred + u8 error_code; + u8 padding1; + + u32 missed_PDC0; + u32 missed_PDC1; InterruptId slot[0x34]; ///< Interrupt ID slots }; diff --git a/src/core/hle/service/gsp_lcd.cpp b/src/core/hle/service/gsp_lcd.cpp new file mode 100644 index 000000000..d63fa1ee2 --- /dev/null +++ b/src/core/hle/service/gsp_lcd.cpp @@ -0,0 +1,26 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + + +#include "common/log.h" +#include "common/bit_field.h" + +#include "core/hle/service/gsp_lcd.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace GSP_LCD + +namespace GSP_LCD { + +/*const Interface::FunctionInfo FunctionTable[] = { +};*/ + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + //Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/gsp_lcd.h b/src/core/hle/service/gsp_lcd.h new file mode 100644 index 000000000..56b3cfe86 --- /dev/null +++ b/src/core/hle/service/gsp_lcd.h @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace GSP_LCD + +namespace GSP_LCD { + +/// Interface to "gsp::Lcd" service +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "gsp::Lcd"; + } +}; + +} // namespace diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp new file mode 100644 index 000000000..7cb01729e --- /dev/null +++ b/src/core/hle/service/hid/hid.cpp @@ -0,0 +1,142 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/hid/hid.h" + +#include "core/arm/arm_interface.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/hle.h" + +namespace Service { +namespace HID { + +Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem = nullptr; + +Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1; +Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2; +Kernel::SharedPtr<Kernel::Event> g_event_accelerometer; +Kernel::SharedPtr<Kernel::Event> g_event_gyroscope; +Kernel::SharedPtr<Kernel::Event> g_event_debug_pad; + +// Next Pad state update information +static PadState next_state = {{0}}; +static u32 next_index = 0; +static s16 next_circle_x = 0; +static s16 next_circle_y = 0; + +/** + * Gets a pointer to the PadData structure inside HID shared memory + */ +static inline PadData* GetPadData() { + if (g_shared_mem == nullptr) + return nullptr; + return reinterpret_cast<PadData*>(g_shared_mem->GetPointer().ValueOr(nullptr)); +} + +/** + * Circle Pad from keys. + * + * This is implemented as "pushed all the way to an edge (max) or centered (0)". + * + * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions. + */ +static void UpdateNextCirclePadState() { + static const s16 max_value = 0x9C; + next_circle_x = next_state.circle_left ? -max_value : 0x0; + next_circle_x += next_state.circle_right ? max_value : 0x0; + next_circle_y = next_state.circle_down ? -max_value : 0x0; + next_circle_y += next_state.circle_up ? max_value : 0x0; +} + +/** + * Sets a Pad state (button or button combo) as pressed + */ +void PadButtonPress(const PadState& pad_state) { + next_state.hex |= pad_state.hex; + UpdateNextCirclePadState(); +} + +/** + * Sets a Pad state (button or button combo) as released + */ +void PadButtonRelease(const PadState& pad_state) { + next_state.hex &= ~pad_state.hex; + UpdateNextCirclePadState(); +} + +/** + * Called after all Pad changes to be included in this update have been made, + * including both Pad key changes and analog circle Pad changes. + */ +void PadUpdateComplete() { + PadData* pad_data = GetPadData(); + + if (pad_data == nullptr) { + return; + } + + // Update PadData struct + pad_data->current_state.hex = next_state.hex; + pad_data->index = next_index; + next_index = (next_index + 1) % pad_data->entries.size(); + + // Get the previous Pad state + u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size(); + PadState old_state = pad_data->entries[last_entry_index].current_state; + + // Compute bitmask with 1s for bits different from the old state + PadState changed; + changed.hex = (next_state.hex ^ old_state.hex); + + // Compute what was added + PadState additions; + additions.hex = changed.hex & next_state.hex; + + // Compute what was removed + PadState removals; + removals.hex = changed.hex & old_state.hex; + + // Get the current Pad entry + PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index]; + + // Update entry properties + current_pad_entry->current_state.hex = next_state.hex; + current_pad_entry->delta_additions.hex = additions.hex; + current_pad_entry->delta_removals.hex = removals.hex; + + // Set circle Pad + current_pad_entry->circle_pad_x = next_circle_x; + current_pad_entry->circle_pad_y = next_circle_y; + + // If we just updated index 0, provide a new timestamp + if (pad_data->index == 0) { + pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks; + pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks(); + } + + // Signal both handles when there's an update to Pad or touch + g_event_pad_or_touch_1->Signal(); + g_event_pad_or_touch_2->Signal(); +} + +void HIDInit() { + using namespace Kernel; + + g_shared_mem = SharedMemory::Create("HID:SharedMem"); + + // Create event handles + g_event_pad_or_touch_1 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch1"); + g_event_pad_or_touch_2 = Event::Create(RESETTYPE_ONESHOT, "HID:EventPadOrTouch2"); + g_event_accelerometer = Event::Create(RESETTYPE_ONESHOT, "HID:EventAccelerometer"); + g_event_gyroscope = Event::Create(RESETTYPE_ONESHOT, "HID:EventGyroscope"); + g_event_debug_pad = Event::Create(RESETTYPE_ONESHOT, "HID:EventDebugPad"); +} + +void HIDShutdown() { + +} + +} +} diff --git a/src/core/hle/service/hid_user.h b/src/core/hle/service/hid/hid.h index 5b96dda60..2116d2ca3 100644 --- a/src/core/hle/service/hid_user.h +++ b/src/core/hle/service/hid/hid.h @@ -1,19 +1,31 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2015 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once -#include "core/hle/service/service.h" +#include <array> + +#include "core/hle/kernel/kernel.h" #include "common/bit_field.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HID_User +namespace Kernel { + class SharedMemory; + class Event; +} + +namespace Service { +namespace HID { -// This service is used for interfacing to physical user controls. -// Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad. +// Handle to shared memory region designated to HID_User service +extern Kernel::SharedPtr<Kernel::SharedMemory> g_shared_mem; -namespace HID_User { +// Event handles +extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_1; +extern Kernel::SharedPtr<Kernel::Event> g_event_pad_or_touch_2; +extern Kernel::SharedPtr<Kernel::Event> g_event_accelerometer; +extern Kernel::SharedPtr<Kernel::Event> g_event_gyroscope; +extern Kernel::SharedPtr<Kernel::Event> g_event_debug_pad; /** * Structure of a Pad controller state. @@ -97,16 +109,8 @@ void PadButtonPress(const PadState& pad_state); void PadButtonRelease(const PadState& pad_state); void PadUpdateComplete(); -/** - * HID service interface. - */ -class Interface : public Service::Interface { -public: - Interface(); - - std::string GetPortName() const override { - return "hid:USER"; - } -}; +void HIDInit(); +void HIDShutdown(); -} // namespace +} +} diff --git a/src/core/hle/service/hid/hid_spvr.cpp b/src/core/hle/service/hid/hid_spvr.cpp new file mode 100644 index 000000000..054aa8b59 --- /dev/null +++ b/src/core/hle/service/hid/hid_spvr.cpp @@ -0,0 +1,38 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/hid/hid_spvr.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace HID_SPVR + +namespace HID_User { + extern void GetIPCHandles(Service::Interface* self); +} + +namespace HID_SPVR { + +const Interface::FunctionInfo FunctionTable[] = { + {0x000A0000, HID_User::GetIPCHandles, "GetIPCHandles"}, + {0x000B0000, nullptr, "StartAnalogStickCalibration"}, + {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, + {0x00110000, nullptr, "EnableAccelerometer"}, + {0x00120000, nullptr, "DisableAccelerometer"}, + {0x00130000, nullptr, "EnableGyroscopeLow"}, + {0x00140000, nullptr, "DisableGyroscopeLow"}, + {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, + {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, + {0x00170000, nullptr, "GetSoundVolume"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/hid/hid_spvr.h b/src/core/hle/service/hid/hid_spvr.h new file mode 100644 index 000000000..53ddc8569 --- /dev/null +++ b/src/core/hle/service/hid/hid_spvr.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace HID_SPVR + +namespace HID_SPVR { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "hid:SPVR"; + } +}; + +} // namespace diff --git a/src/core/hle/service/hid/hid_user.cpp b/src/core/hle/service/hid/hid_user.cpp new file mode 100644 index 000000000..68edafebb --- /dev/null +++ b/src/core/hle/service/hid/hid_user.cpp @@ -0,0 +1,78 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" + +#include "core/hle/hle.h" +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/hid/hid.h" +#include "hid_user.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace HID_User + +namespace HID_User { + + +// TODO(peachum): +// Add a method for setting analog input from joystick device for the circle Pad. +// +// This method should: +// * Be called after both PadButton<Press, Release>(). +// * Be called before PadUpdateComplete() +// * Set current PadEntry.circle_pad_<axis> using analog data +// * Set PadData.raw_circle_pad_data +// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41 +// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41 +// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 +// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 + + +/** + * HID_User::GetIPCHandles service function + * Inputs: + * None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Unused + * 3 : Handle to HID_User shared memory + * 4 : Event signaled by HID_User + * 5 : Event signaled by HID_User + * 6 : Event signaled by HID_User + * 7 : Gyroscope event + * 8 : Event signaled by HID_User + */ +void GetIPCHandles(Service::Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = 0; // No error + // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) + cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::g_shared_mem).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_1).MoveFrom(); + cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::g_event_pad_or_touch_2).MoveFrom(); + cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::g_event_accelerometer).MoveFrom(); + cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::g_event_gyroscope).MoveFrom(); + cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::g_event_debug_pad).MoveFrom(); +} + +const Interface::FunctionInfo FunctionTable[] = { + {0x000A0000, GetIPCHandles, "GetIPCHandles"}, + {0x00110000, nullptr, "EnableAccelerometer"}, + {0x00120000, nullptr, "DisableAccelerometer"}, + {0x00130000, nullptr, "EnableGyroscopeLow"}, + {0x00140000, nullptr, "DisableGyroscopeLow"}, + {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, + {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, + {0x00170000, nullptr, "GetSoundVolume"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/hid/hid_user.h b/src/core/hle/service/hid/hid_user.h new file mode 100644 index 000000000..1d9929e67 --- /dev/null +++ b/src/core/hle/service/hid/hid_user.h @@ -0,0 +1,29 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace HID_User + +// This service is used for interfacing to physical user controls. +// Uses include game pad controls, touchscreen, accelerometers, gyroscopes, and debug pad. + +namespace HID_User { + +/** + * HID service interface. + */ +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "hid:USER"; + } +}; + +} // namespace diff --git a/src/core/hle/service/hid_user.cpp b/src/core/hle/service/hid_user.cpp deleted file mode 100644 index 1403b1de9..000000000 --- a/src/core/hle/service/hid_user.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/log.h" - -#include "core/arm/arm_interface.h" -#include "core/hle/hle.h" -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/shared_memory.h" -#include "hid_user.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Namespace HID_User - -namespace HID_User { - -// Handle to shared memory region designated to HID_User service -static Handle shared_mem = 0; - -// Event handles -static Handle event_pad_or_touch_1 = 0; -static Handle event_pad_or_touch_2 = 0; -static Handle event_accelerometer = 0; -static Handle event_gyroscope = 0; -static Handle event_debug_pad = 0; - -// Next Pad state update information -static PadState next_state = {{0}}; -static u32 next_index = 0; -static s16 next_circle_x = 0; -static s16 next_circle_y = 0; - -/** - * Gets a pointer to the PadData structure inside HID shared memory - */ -static inline PadData* GetPadData() { - return reinterpret_cast<PadData*>(Kernel::GetSharedMemoryPointer(shared_mem, 0).ValueOr(nullptr)); -} - -/** - * Circle Pad from keys. - * - * This is implemented as "pushed all the way to an edge (max) or centered (0)". - * - * Indicate the circle pad is pushed completely to the edge in 1 of 8 directions. - */ -static void UpdateNextCirclePadState() { - static const s16 max_value = 0x9C; - next_circle_x = next_state.circle_left ? -max_value : 0x0; - next_circle_x += next_state.circle_right ? max_value : 0x0; - next_circle_y = next_state.circle_down ? -max_value : 0x0; - next_circle_y += next_state.circle_up ? max_value : 0x0; -} - -/** - * Sets a Pad state (button or button combo) as pressed - */ -void PadButtonPress(const PadState& pad_state) { - next_state.hex |= pad_state.hex; - UpdateNextCirclePadState(); -} - -/** - * Sets a Pad state (button or button combo) as released - */ -void PadButtonRelease(const PadState& pad_state) { - next_state.hex &= ~pad_state.hex; - UpdateNextCirclePadState(); -} - -/** - * Called after all Pad changes to be included in this update have been made, - * including both Pad key changes and analog circle Pad changes. - */ -void PadUpdateComplete() { - PadData* pad_data = GetPadData(); - - if (pad_data == nullptr) { - return; - } - - // Update PadData struct - pad_data->current_state.hex = next_state.hex; - pad_data->index = next_index; - next_index = (next_index + 1) % pad_data->entries.size(); - - // Get the previous Pad state - u32 last_entry_index = (pad_data->index - 1) % pad_data->entries.size(); - PadState old_state = pad_data->entries[last_entry_index].current_state; - - // Compute bitmask with 1s for bits different from the old state - PadState changed; - changed.hex = (next_state.hex ^ old_state.hex); - - // Compute what was added - PadState additions; - additions.hex = changed.hex & next_state.hex; - - // Compute what was removed - PadState removals; - removals.hex = changed.hex & old_state.hex; - - // Get the current Pad entry - PadDataEntry* current_pad_entry = &pad_data->entries[pad_data->index]; - - // Update entry properties - current_pad_entry->current_state.hex = next_state.hex; - current_pad_entry->delta_additions.hex = additions.hex; - current_pad_entry->delta_removals.hex = removals.hex; - - // Set circle Pad - current_pad_entry->circle_pad_x = next_circle_x; - current_pad_entry->circle_pad_y = next_circle_y; - - // If we just updated index 0, provide a new timestamp - if (pad_data->index == 0) { - pad_data->index_reset_ticks_previous = pad_data->index_reset_ticks; - pad_data->index_reset_ticks = (s64)Core::g_app_core->GetTicks(); - } - - // Signal both handles when there's an update to Pad or touch - Kernel::SignalEvent(event_pad_or_touch_1); - Kernel::SignalEvent(event_pad_or_touch_2); -} - - -// TODO(peachum): -// Add a method for setting analog input from joystick device for the circle Pad. -// -// This method should: -// * Be called after both PadButton<Press, Release>(). -// * Be called before PadUpdateComplete() -// * Set current PadEntry.circle_pad_<axis> using analog data -// * Set PadData.raw_circle_pad_data -// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_x >= 41 -// * Set PadData.current_state.circle_up = 1 if current PadEntry.circle_pad_y >= 41 -// * Set PadData.current_state.circle_left = 1 if current PadEntry.circle_pad_x <= -41 -// * Set PadData.current_state.circle_right = 1 if current PadEntry.circle_pad_y <= -41 - - -/** - * HID_User::GetIPCHandles service function - * Inputs: - * None - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Unused - * 3 : Handle to HID_User shared memory - * 4 : Event signaled by HID_User - * 5 : Event signaled by HID_User - * 6 : Event signaled by HID_User - * 7 : Gyroscope event - * 8 : Event signaled by HID_User - */ -static void GetIPCHandles(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = 0; // No error - cmd_buff[3] = shared_mem; - cmd_buff[4] = event_pad_or_touch_1; - cmd_buff[5] = event_pad_or_touch_2; - cmd_buff[6] = event_accelerometer; - cmd_buff[7] = event_gyroscope; - cmd_buff[8] = event_debug_pad; -} - -const Interface::FunctionInfo FunctionTable[] = { - {0x000A0000, GetIPCHandles, "GetIPCHandles"}, - {0x000B0000, nullptr, "StartAnalogStickCalibration"}, - {0x000E0000, nullptr, "GetAnalogStickCalibrateParam"}, - {0x00110000, nullptr, "EnableAccelerometer"}, - {0x00120000, nullptr, "DisableAccelerometer"}, - {0x00130000, nullptr, "EnableGyroscopeLow"}, - {0x00140000, nullptr, "DisableGyroscopeLow"}, - {0x00150000, nullptr, "GetGyroscopeLowRawToDpsCoefficient"}, - {0x00160000, nullptr, "GetGyroscopeLowCalibrateParam"}, - {0x00170000, nullptr, "GetSoundVolume"}, -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Interface class - -Interface::Interface() { - shared_mem = Kernel::CreateSharedMemory("HID_User:SharedMem"); // Create shared memory object - - // Create event handles - event_pad_or_touch_1 = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventPadOrTouch1"); - event_pad_or_touch_2 = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventPadOrTouch2"); - event_accelerometer = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventAccelerometer"); - event_gyroscope = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventGyroscope"); - event_debug_pad = Kernel::CreateEvent(RESETTYPE_ONESHOT, "HID_User:EventDebugPad"); - - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); -} - -} // namespace diff --git a/src/core/hle/service/http_c.cpp b/src/core/hle/service/http_c.cpp index d0bff552f..6595ca572 100644 --- a/src/core/hle/service/http_c.cpp +++ b/src/core/hle/service/http_c.cpp @@ -58,7 +58,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ir_rst.cpp b/src/core/hle/service/ir_rst.cpp index d49bd5335..31da8e160 100644 --- a/src/core/hle/service/ir_rst.cpp +++ b/src/core/hle/service/ir_rst.cpp @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ir_u.cpp b/src/core/hle/service/ir_u.cpp index da6f38e41..7fa233048 100644 --- a/src/core/hle/service/ir_u.cpp +++ b/src/core/hle/service/ir_u.cpp @@ -36,7 +36,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ldr_ro.cpp b/src/core/hle/service/ldr_ro.cpp index 7d6e2e8e8..459717fff 100644 --- a/src/core/hle/service/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro.cpp @@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 82bce9180..af967b5b6 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -34,7 +34,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ndm_u.cpp b/src/core/hle/service/ndm_u.cpp index 0f03de6ae..df3c97193 100644 --- a/src/core/hle/service/ndm_u.cpp +++ b/src/core/hle/service/ndm_u.cpp @@ -24,7 +24,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/news_s.cpp b/src/core/hle/service/news_s.cpp new file mode 100644 index 000000000..d7537875b --- /dev/null +++ b/src/core/hle/service/news_s.cpp @@ -0,0 +1,25 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/news_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NEWS_S + +namespace NEWS_S { + +const Interface::FunctionInfo FunctionTable[] = { + {0x000100C6, nullptr, "AddNotification"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/news_s.h b/src/core/hle/service/news_s.h new file mode 100644 index 000000000..f8b4636d5 --- /dev/null +++ b/src/core/hle/service/news_s.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NEWS_S + +namespace NEWS_S { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "news:s"; + } +}; + +} // namespace diff --git a/src/core/hle/service/news_u.cpp b/src/core/hle/service/news_u.cpp index b5adad4c6..a9e161c23 100644 --- a/src/core/hle/service/news_u.cpp +++ b/src/core/hle/service/news_u.cpp @@ -19,7 +19,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/nim_aoc.cpp b/src/core/hle/service/nim_aoc.cpp index 17d1c4ff5..ab2ef4429 100644 --- a/src/core/hle/service/nim_aoc.cpp +++ b/src/core/hle/service/nim_aoc.cpp @@ -25,7 +25,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp new file mode 100644 index 000000000..5cf3e2039 --- /dev/null +++ b/src/core/hle/service/ns_s.cpp @@ -0,0 +1,27 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + + +#include "common/common.h" + +#include "core/hle/hle.h" +#include "core/hle/service/ns_s.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NS_S + +namespace NS_S { + +const Interface::FunctionInfo FunctionTable[] = { + {0x000200C0, nullptr, "LaunchTitle"}, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns_s.h new file mode 100644 index 000000000..8d8e849b8 --- /dev/null +++ b/src/core/hle/service/ns_s.h @@ -0,0 +1,24 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace NS_S + +namespace NS_S { + +/// Interface to "NS:S" service +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "ns:s"; + } +}; + +} // namespace diff --git a/src/core/hle/service/nwm_uds.cpp b/src/core/hle/service/nwm_uds.cpp index ce456a966..61fcb54ce 100644 --- a/src/core/hle/service/nwm_uds.cpp +++ b/src/core/hle/service/nwm_uds.cpp @@ -26,7 +26,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/pm_app.cpp b/src/core/hle/service/pm_app.cpp index 529dccafb..d61eaf80f 100644 --- a/src/core/hle/service/pm_app.cpp +++ b/src/core/hle/service/pm_app.cpp @@ -26,7 +26,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ptm_play.cpp b/src/core/hle/service/ptm_play.cpp new file mode 100644 index 000000000..b357057fd --- /dev/null +++ b/src/core/hle/service/ptm_play.cpp @@ -0,0 +1,28 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "core/hle/hle.h" +#include "core/hle/service/ptm_play.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace PTM_PLAY + +namespace PTM_PLAY { + +const Interface::FunctionInfo FunctionTable[] = { + { 0x08070082, nullptr, "GetPlayHistory" }, + { 0x08080000, nullptr, "GetPlayHistoryStart" }, + { 0x08090000, nullptr, "GetPlayHistoryLength" }, + { 0x080B0080, nullptr, "CalcPlayHistoryStart" }, +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/ptm_play.h b/src/core/hle/service/ptm_play.h new file mode 100644 index 000000000..2f4f0d6fd --- /dev/null +++ b/src/core/hle/service/ptm_play.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace PTM_PLAY + +namespace PTM_PLAY { + +class Interface : public Service::Interface { +public: + Interface(); + +std::string GetPortName() const override { + return "ptm:play"; +} +}; + +} // namespace diff --git a/src/core/hle/service/ptm_sysm.cpp b/src/core/hle/service/ptm_sysm.cpp new file mode 100644 index 000000000..b6f688de3 --- /dev/null +++ b/src/core/hle/service/ptm_sysm.cpp @@ -0,0 +1,56 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/log.h" +#include "common/make_unique.h" +#include "core/file_sys/archive_extsavedata.h" +#include "core/hle/hle.h" +#include "core/hle/service/ptm_sysm.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace PTM_SYSM + +namespace PTM_SYSM { + +const Interface::FunctionInfo FunctionTable[] = { + {0x040100C0, nullptr, "SetRtcAlarmEx"}, + {0x04020042, nullptr, "ReplySleepQuery"}, + {0x04030042, nullptr, "NotifySleepPreparationComplete"}, + {0x04040102, nullptr, "SetWakeupTrigger"}, + {0x04050000, nullptr, "GetAwakeReason"}, + {0x04060000, nullptr, "RequestSleep"}, + {0x040700C0, nullptr, "ShutdownAsync"}, + {0x04080000, nullptr, "Awake"}, + {0x04090080, nullptr, "RebootAsync"}, + {0x040A0000, nullptr, "CheckNew3DS"}, + {0x08010640, nullptr, "SetInfoLEDPattern"}, + {0x08020040, nullptr, "SetInfoLEDPatternHeader"}, + {0x08030000, nullptr, "GetInfoLEDStatus"}, + {0x08040040, nullptr, "SetBatteryEmptyLEDPattern"}, + {0x08050000, nullptr, "ClearStepHistory"}, + {0x080600C2, nullptr, "SetStepHistory"}, + {0x08070082, nullptr, "GetPlayHistory"}, + {0x08080000, nullptr, "GetPlayHistoryStart"}, + {0x08090000, nullptr, "GetPlayHistoryLength"}, + {0x080A0000, nullptr, "ClearPlayHistory"}, + {0x080B0080, nullptr, "CalcPlayHistoryStart"}, + {0x080C0080, nullptr, "SetUserTime"}, + {0x080D0000, nullptr, "InvalidateSystemTime"}, + {0x080E0140, nullptr, "NotifyPlayEvent"}, + {0x080F0000, nullptr, "IsLegacyPowerOff"}, + {0x08100000, nullptr, "ClearLegacyPowerOff"}, + {0x08110000, nullptr, "GetShellStatus"}, + {0x08120000, nullptr, "IsShutdownByBatteryEmpty"}, + {0x08130000, nullptr, "FormatSavedata"}, + {0x08140000, nullptr, "GetLegacyJumpProhibitedFlag"} +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Interface class + +Interface::Interface() { + Register(FunctionTable); +} + +} // namespace diff --git a/src/core/hle/service/ptm_sysm.h b/src/core/hle/service/ptm_sysm.h new file mode 100644 index 000000000..0f267b214 --- /dev/null +++ b/src/core/hle/service/ptm_sysm.h @@ -0,0 +1,23 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Namespace PTM_SYSM + +namespace PTM_SYSM { + +class Interface : public Service::Interface { +public: + Interface(); + + std::string GetPortName() const override { + return "ptm:sysm"; + } +}; + +} // namespace diff --git a/src/core/hle/service/ptm_u.cpp b/src/core/hle/service/ptm_u.cpp index 753180add..7b465a348 100644 --- a/src/core/hle/service/ptm_u.cpp +++ b/src/core/hle/service/ptm_u.cpp @@ -137,7 +137,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); // Create the SharedExtSaveData archive 0xF000000B and the gamecoin.dat file // TODO(Subv): In the future we should use the FS service to query this archive std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 33c29a4a0..e0979ea5d 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -10,30 +10,43 @@ #include "core/hle/service/act_u.h" #include "core/hle/service/am_app.h" #include "core/hle/service/am_net.h" +#include "core/hle/service/am_sys.h" #include "core/hle/service/apt_a.h" +#include "core/hle/service/apt_s.h" #include "core/hle/service/apt_u.h" +#include "core/hle/service/boss_p.h" #include "core/hle/service/boss_u.h" +#include "core/hle/service/cam_u.h" #include "core/hle/service/cecd_u.h" +#include "core/hle/service/cecd_s.h" #include "core/hle/service/cfg/cfg_i.h" +#include "core/hle/service/cfg/cfg_s.h" #include "core/hle/service/cfg/cfg_u.h" #include "core/hle/service/csnd_snd.h" #include "core/hle/service/dsp_dsp.h" #include "core/hle/service/err_f.h" #include "core/hle/service/fs/fs_user.h" +#include "core/hle/service/frd_a.h" #include "core/hle/service/frd_u.h" #include "core/hle/service/gsp_gpu.h" -#include "core/hle/service/hid_user.h" +#include "core/hle/service/hid/hid_spvr.h" +#include "core/hle/service/hid/hid_user.h" +#include "core/hle/service/gsp_lcd.h" #include "core/hle/service/http_c.h" #include "core/hle/service/ir_rst.h" #include "core/hle/service/ir_u.h" #include "core/hle/service/ldr_ro.h" #include "core/hle/service/mic_u.h" #include "core/hle/service/ndm_u.h" +#include "core/hle/service/news_s.h" #include "core/hle/service/news_u.h" #include "core/hle/service/nim_aoc.h" +#include "core/hle/service/ns_s.h" #include "core/hle/service/nwm_uds.h" #include "core/hle/service/pm_app.h" +#include "core/hle/service/ptm_play.h" #include "core/hle/service/ptm_u.h" +#include "core/hle/service/ptm_sysm.h" #include "core/hle/service/soc_u.h" #include "core/hle/service/srv.h" #include "core/hle/service/ssl_c.h" @@ -41,83 +54,76 @@ namespace Service { -Manager* g_manager = nullptr; ///< Service manager +std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; +std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; //////////////////////////////////////////////////////////////////////////////////////////////////// -// Service Manager class - -void Manager::AddService(Interface* service) { - // TOOD(yuriks): Fix error reporting - m_port_map[service->GetPortName()] = Kernel::g_handle_table.Create(service).ValueOr(INVALID_HANDLE); - m_services.push_back(service); -} - -void Manager::DeleteService(const std::string& port_name) { - Interface* service = FetchFromPortName(port_name); - m_services.erase(std::remove(m_services.begin(), m_services.end(), service), m_services.end()); - m_port_map.erase(port_name); -} +// Module interface -Interface* Manager::FetchFromHandle(Handle handle) { - // TODO(yuriks): This function is very suspicious and should probably be exterminated. - return Kernel::g_handle_table.Get<Interface>(handle).get(); +static void AddNamedPort(Interface* interface) { + g_kernel_named_ports.emplace(interface->GetPortName(), interface); } -Interface* Manager::FetchFromPortName(const std::string& port_name) { - auto itr = m_port_map.find(port_name); - if (itr == m_port_map.end()) { - return nullptr; - } - return FetchFromHandle(itr->second); +static void AddService(Interface* interface) { + g_srv_services.emplace(interface->GetPortName(), interface); } - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Module interface - /// Initialize ServiceManager void Init() { - g_manager = new Manager; - - g_manager->AddService(new SRV::Interface); - g_manager->AddService(new AC_U::Interface); - g_manager->AddService(new ACT_U::Interface); - g_manager->AddService(new AM_APP::Interface); - g_manager->AddService(new AM_NET::Interface); - g_manager->AddService(new APT_A::Interface); - g_manager->AddService(new APT_U::Interface); - g_manager->AddService(new BOSS_U::Interface); - g_manager->AddService(new CECD_U::Interface); - g_manager->AddService(new CFG_I::Interface); - g_manager->AddService(new CFG_U::Interface); - g_manager->AddService(new CSND_SND::Interface); - g_manager->AddService(new DSP_DSP::Interface); - g_manager->AddService(new ERR_F::Interface); - g_manager->AddService(new FRD_U::Interface); - g_manager->AddService(new FS::FSUserInterface); - g_manager->AddService(new GSP_GPU::Interface); - g_manager->AddService(new HID_User::Interface); - g_manager->AddService(new HTTP_C::Interface); - g_manager->AddService(new IR_RST::Interface); - g_manager->AddService(new IR_U::Interface); - g_manager->AddService(new LDR_RO::Interface); - g_manager->AddService(new MIC_U::Interface); - g_manager->AddService(new NDM_U::Interface); - g_manager->AddService(new NEWS_U::Interface); - g_manager->AddService(new NIM_AOC::Interface); - g_manager->AddService(new NWM_UDS::Interface); - g_manager->AddService(new PM_APP::Interface); - g_manager->AddService(new PTM_U::Interface); - g_manager->AddService(new SOC_U::Interface); - g_manager->AddService(new SSL_C::Interface); - g_manager->AddService(new Y2R_U::Interface); + AddNamedPort(new SRV::Interface); + + AddService(new AC_U::Interface); + AddService(new ACT_U::Interface); + AddService(new AM_APP::Interface); + AddService(new AM_NET::Interface); + AddService(new AM_SYS::Interface); + AddService(new APT_A::Interface); + AddService(new APT_S::Interface); + AddService(new APT_U::Interface); + AddService(new BOSS_P::Interface); + AddService(new BOSS_U::Interface); + AddService(new CAM_U::Interface); + AddService(new CECD_S::Interface); + AddService(new CECD_U::Interface); + AddService(new CFG_I::Interface); + AddService(new CFG_S::Interface); + AddService(new CFG_U::Interface); + AddService(new CSND_SND::Interface); + AddService(new DSP_DSP::Interface); + AddService(new ERR_F::Interface); + AddService(new FRD_A::Interface); + AddService(new FRD_U::Interface); + AddService(new FS::FSUserInterface); + AddService(new GSP_GPU::Interface); + AddService(new GSP_LCD::Interface); + AddService(new HID_User::Interface); + AddService(new HID_SPVR::Interface); + AddService(new HTTP_C::Interface); + AddService(new IR_RST::Interface); + AddService(new IR_U::Interface); + AddService(new LDR_RO::Interface); + AddService(new MIC_U::Interface); + AddService(new NDM_U::Interface); + AddService(new NEWS_S::Interface); + AddService(new NEWS_U::Interface); + AddService(new NIM_AOC::Interface); + AddService(new NS_S::Interface); + AddService(new NWM_UDS::Interface); + AddService(new PM_APP::Interface); + AddService(new PTM_PLAY::Interface); + AddService(new PTM_U::Interface); + AddService(new PTM_SYSM::Interface); + AddService(new SOC_U::Interface); + AddService(new SSL_C::Interface); + AddService(new Y2R_U::Interface); LOG_DEBUG(Service, "initialized OK"); } /// Shutdown ServiceManager void Shutdown() { - delete g_manager; + g_srv_services.clear(); + 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 e75d5008b..3370f9f9b 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -5,9 +5,11 @@ #pragma once #include <algorithm> -#include <vector> -#include <map> #include <string> +#include <unordered_map> +#include <vector> + +#include <boost/container/flat_map.hpp> #include "common/common.h" #include "common/string_util.h" @@ -27,7 +29,7 @@ static const int kMaxPortSize = 8; ///< Maximum size of a port name (8 character class Manager; /// Interface to a CTROS service -class Interface : public Kernel::Session { +class Interface : public Kernel::Session { // TODO(yuriks): An "Interface" being a Kernel::Object is mostly non-sense. Interface should be // just something that encapsulates a session and acts as a helper to implement service // processes. @@ -38,11 +40,11 @@ class Interface : public Kernel::Session { * Creates a function string for logging, complete with the name (or header code, depending * on what's passed in) the port name, and all the cmd_buff arguments. */ - std::string MakeFunctionString(const std::string& name, const std::string& port_name, const u32* cmd_buff) { + std::string MakeFunctionString(const char* name, const char* port_name, const u32* cmd_buff) { // Number of params == bits 0-5 + bits 6-11 int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F); - std::string function_string = Common::StringFromFormat("function '%s': port=%s", name.c_str(), port_name.c_str()); + std::string function_string = Common::StringFromFormat("function '%s': port=%s", name, port_name); for (int i = 1; i <= num_params; ++i) { function_string += Common::StringFromFormat(", cmd_buff[%i]=%u", i, cmd_buff[i]); } @@ -57,7 +59,7 @@ public: struct FunctionInfo { u32 id; Function func; - std::string name; + const char* name; }; /** @@ -68,34 +70,19 @@ public: return "[UNKNOWN SERVICE PORT]"; } - /// Allocates a new handle for the service - Handle CreateHandle(Kernel::Object *obj) { - // TODO(yuriks): Fix error reporting - Handle handle = Kernel::g_handle_table.Create(obj).ValueOr(INVALID_HANDLE); - m_handles.push_back(handle); - return handle; - } - - /// Frees a handle from the service - template <class T> - void DeleteHandle(const Handle handle) { - Kernel::g_handle_table.Close(handle); - m_handles.erase(std::remove(m_handles.begin(), m_handles.end(), handle), m_handles.end()); - } - ResultVal<bool> SyncRequest() override { u32* cmd_buff = Kernel::GetCommandBuffer(); auto itr = m_functions.find(cmd_buff[0]); if (itr == m_functions.end() || itr->second.func == nullptr) { std::string function_name = (itr == m_functions.end()) ? Common::StringFromFormat("0x%08X", cmd_buff[0]) : itr->second.name; - LOG_ERROR(Service, "%s %s", "unknown/unimplemented", MakeFunctionString(function_name, GetPortName(), cmd_buff).c_str()); + LOG_ERROR(Service, "unknown / unimplemented %s", MakeFunctionString(function_name.c_str(), GetPortName().c_str(), cmd_buff).c_str()); // TODO(bunnei): Hack - ignore error cmd_buff[1] = 0; return MakeResult<bool>(false); } else { - LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName(), cmd_buff).c_str()); + LOG_TRACE(Service, "%s", MakeFunctionString(itr->second.name, GetPortName().c_str(), cmd_buff).c_str()); } itr->second.func(this); @@ -108,37 +95,18 @@ protected: /** * Registers the functions in the service */ - void Register(const FunctionInfo* functions, int len) { - for (int i = 0; i < len; i++) { - m_functions[functions[i].id] = functions[i]; + template <size_t N> + void Register(const FunctionInfo (&functions)[N]) { + m_functions.reserve(N); + for (auto& fn : functions) { + // Usually this array is sorted by id already, so hint to instead at the end + m_functions.emplace_hint(m_functions.cend(), fn.id, fn); } } private: + boost::container::flat_map<u32, FunctionInfo> m_functions; - std::vector<Handle> m_handles; - std::map<u32, FunctionInfo> m_functions; - -}; - -/// Simple class to manage accessing services from ports and UID handles -class Manager { -public: - /// Add a service to the manager - void AddService(Interface* service); - - /// Removes a service from the manager - void DeleteService(const std::string& port_name); - - /// Get a Service Interface from its Handle - Interface* FetchFromHandle(Handle handle); - - /// Get a Service Interface from its port - Interface* FetchFromPortName(const std::string& port_name); - -private: - std::vector<Interface*> m_services; - std::map<std::string, u32> m_port_map; }; /// Initialize ServiceManager @@ -147,8 +115,9 @@ void Init(); /// Shutdown ServiceManager void Shutdown(); - -extern Manager* g_manager; ///< Service manager - +/// Map of named ports managed by the kernel, which can be retrieved using the ConnectToPort SVC. +extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_kernel_named_ports; +/// Map of services registered with the "srv:" service, retrieved using GetServiceHandle. +extern std::unordered_map<std::string, Kernel::SharedPtr<Interface>> g_srv_services; } // namespace diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index f502c6afe..414c53c54 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -7,6 +7,19 @@ #if EMU_PLATFORM == PLATFORM_WINDOWS #include <winsock2.h> #include <ws2tcpip.h> + +// MinGW does not define several errno constants +#ifndef _MSC_VER +#define EBADMSG 104 +#define ENODATA 120 +#define ENOMSG 122 +#define ENOSR 124 +#define ENOSTR 125 +#define ETIME 137 +#define EIDRM 2001 +#define ENOLINK 2002 +#endif // _MSC_VER + #else #include <sys/socket.h> #include <netinet/in.h> @@ -721,7 +734,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } Interface::~Interface() { diff --git a/src/core/hle/service/srv.cpp b/src/core/hle/service/srv.cpp index ac5f30a28..cc59a03ce 100644 --- a/src/core/hle/service/srv.cpp +++ b/src/core/hle/service/srv.cpp @@ -11,7 +11,7 @@ namespace SRV { -static Handle g_event_handle = 0; +static Kernel::SharedPtr<Kernel::Event> event_handle; static void Initialize(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -23,11 +23,11 @@ static void GetProcSemaphore(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); // TODO(bunnei): Change to a semaphore once these have been implemented - g_event_handle = Kernel::CreateEvent(RESETTYPE_ONESHOT, "SRV:Event"); - Kernel::SetEventLocked(g_event_handle, false); + event_handle = Kernel::Event::Create(RESETTYPE_ONESHOT, "SRV:Event"); + event_handle->Clear(); cmd_buff[1] = 0; // No error - cmd_buff[3] = g_event_handle; + cmd_buff[3] = Kernel::g_handle_table.Create(event_handle).MoveFrom(); } static void GetServiceHandle(Service::Interface* self) { @@ -35,10 +35,10 @@ static void GetServiceHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); std::string port_name = std::string((const char*)&cmd_buff[1], 0, Service::kMaxPortSize); - Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); + auto it = Service::g_srv_services.find(port_name); - if (nullptr != service) { - cmd_buff[3] = service->GetHandle(); + if (it != Service::g_srv_services.end()) { + cmd_buff[3] = Kernel::g_handle_table.Create(it->second).MoveFrom(); LOG_TRACE(Service_SRV, "called port=%s, handle=0x%08X", port_name.c_str(), cmd_buff[3]); } else { LOG_ERROR(Service_SRV, "(UNIMPLEMENTED) called port=%s", port_name.c_str()); @@ -63,7 +63,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/ssl_c.cpp b/src/core/hle/service/ssl_c.cpp index 360516cdf..3f49c1c97 100644 --- a/src/core/hle/service/ssl_c.cpp +++ b/src/core/hle/service/ssl_c.cpp @@ -22,7 +22,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index b3d873ef0..fc76d2721 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -54,7 +54,7 @@ const Interface::FunctionInfo FunctionTable[] = { // Interface class Interface::Interface() { - Register(FunctionTable, ARRAY_SIZE(FunctionTable)); + Register(FunctionTable); } } // namespace diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp new file mode 100644 index 000000000..6033a53b4 --- /dev/null +++ b/src/core/hle/shared_page.cpp @@ -0,0 +1,80 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_types.h" +#include "common/log.h" + +#include "core/core.h" +#include "core/mem_map.h" +#include "core/hle/config_mem.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace SharedPage { + +// helper macro to properly align structure members. +// Calling INSERT_PADDING_BYTES will add a new member variable with a name like "pad121", +// depending on the current source line to make sure variable names are unique. +#define INSERT_PADDING_BYTES_HELPER1(x, y) x ## y +#define INSERT_PADDING_BYTES_HELPER2(x, y) INSERT_PADDING_BYTES_HELPER1(x, y) +#define INSERT_PADDING_BYTES(num_words) u8 INSERT_PADDING_BYTES_HELPER2(pad, __LINE__)[(num_words)] + +// see http://3dbrew.org/wiki/Configuration_Memory#Shared_Memory_Page_For_ARM11_Processes + +#pragma pack(1) +struct DateTime { + u64 date_time; // 0x0 + u64 update_tick; // 0x8 + INSERT_PADDING_BYTES(0x20 - 0x10); // 0x10 +}; + +struct SharedPageDef { + // most of these names are taken from the 3dbrew page linked above. + u32 date_time_selector; // 0x0 + u8 running_hw; // 0x4 + u8 mcu_hw_info; // 0x5: don't know what the acronyms mean + INSERT_PADDING_BYTES(0x20 - 0x6); // 0x6 + DateTime date_time_0; // 0x20 + DateTime date_time_1; // 0x40 + u8 wifi_macaddr[6]; // 0x60 + u8 wifi_unknown1; // 0x66: 3dbrew says these are "Likely wifi hardware related" + u8 wifi_unknown2; // 0x67 + INSERT_PADDING_BYTES(0x80 - 0x68); // 0x68 + float sliderstate_3d; // 0x80 + u8 ledstate_3d; // 0x84 + INSERT_PADDING_BYTES(0xA0 - 0x85); // 0x85 + u64 menu_title_id; // 0xA0 + u64 active_menu_title_id; // 0xA8 + INSERT_PADDING_BYTES(0x1000 - 0xB0); // 0xB0 +}; +#pragma pack() + +static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong"); +static_assert(sizeof(SharedPageDef) == Memory::SHARED_PAGE_SIZE, "Shared page structure size is wrong"); + +static SharedPageDef shared_page; + +template <typename T> +inline void Read(T &var, const u32 addr) { + u32 offset = addr - Memory::SHARED_PAGE_VADDR; + var = *(reinterpret_cast<T*>(((uintptr_t)&shared_page) + offset)); +} + +// Explicitly instantiate template functions because we aren't defining this in the header: +template void Read<u64>(u64 &var, const u32 addr); +template void Read<u32>(u32 &var, const u32 addr); +template void Read<u16>(u16 &var, const u32 addr); +template void Read<u8>(u8 &var, const u32 addr); + +void Set3DSlider(float amount) { + shared_page.sliderstate_3d = amount; + shared_page.ledstate_3d = (amount == 0.0f); // off when non-zero +} + +void Init() { + shared_page.running_hw = 0x1; // product + Set3DSlider(0.0f); +} + +} // namespace diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h new file mode 100644 index 000000000..8f93545ec --- /dev/null +++ b/src/core/hle/shared_page.h @@ -0,0 +1,26 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +/** + * The shared page stores various runtime configuration settings. This memory page is + * read-only for user processes (there is a bit in the header that grants the process + * write access, according to 3dbrew; this is not emulated) + */ + +#include "common/common_types.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace SharedPage { + +template <typename T> +void Read(T &var, const u32 addr); + +void Set3DSlider(float amount); + +void Init(); + +} // namespace diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index d3b4483ca..34a27917f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -26,16 +26,25 @@ // Namespace SVC using Kernel::SharedPtr; +using Kernel::ERR_INVALID_HANDLE; namespace SVC { +const ResultCode ERR_NOT_FOUND(ErrorDescription::NotFound, ErrorModule::Kernel, + ErrorSummary::NotFound, ErrorLevel::Permanent); // 0xD88007FA +const ResultCode ERR_PORT_NAME_TOO_LONG(ErrorDescription(30), ErrorModule::OS, + ErrorSummary::InvalidArgument, ErrorLevel::Usage); // 0xE0E0181E + +/// An invalid result code that is meant to be overwritten when a thread resumes from waiting +const ResultCode RESULT_INVALID(0xDEADC0DE); + enum ControlMemoryOperation { MEMORY_OPERATION_HEAP = 0x00000003, MEMORY_OPERATION_GSP_HEAP = 0x00010003, }; /// Map application or GSP heap memory -static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { +static ResultCode ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, u32 size, u32 permissions) { LOG_TRACE(Kernel_SVC,"called operation=0x%08X, addr0=0x%08X, addr1=0x%08X, size=%08X, permissions=0x%08X", operation, addr0, addr1, size, permissions); @@ -55,147 +64,222 @@ static Result ControlMemory(u32* out_addr, u32 operation, u32 addr0, u32 addr1, default: LOG_ERROR(Kernel_SVC, "unknown operation=0x%08X", operation); } - return 0; + return RESULT_SUCCESS; } /// Maps a memory block to specified address -static Result MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { +static ResultCode MapMemoryBlock(Handle handle, u32 addr, u32 permissions, u32 other_permissions) { + using Kernel::SharedMemory; + using Kernel::MemoryPermission; + LOG_TRACE(Kernel_SVC, "called memblock=0x%08X, addr=0x%08X, mypermissions=0x%08X, otherpermission=%d", handle, addr, permissions, other_permissions); - Kernel::MemoryPermission permissions_type = static_cast<Kernel::MemoryPermission>(permissions); + SharedPtr<SharedMemory> shared_memory = Kernel::g_handle_table.Get<SharedMemory>(handle); + if (shared_memory == nullptr) + return ERR_INVALID_HANDLE; + + MemoryPermission permissions_type = static_cast<MemoryPermission>(permissions); switch (permissions_type) { - case Kernel::MemoryPermission::Read: - case Kernel::MemoryPermission::Write: - case Kernel::MemoryPermission::ReadWrite: - case Kernel::MemoryPermission::Execute: - case Kernel::MemoryPermission::ReadExecute: - case Kernel::MemoryPermission::WriteExecute: - case Kernel::MemoryPermission::ReadWriteExecute: - case Kernel::MemoryPermission::DontCare: - Kernel::MapSharedMemory(handle, addr, permissions_type, - static_cast<Kernel::MemoryPermission>(other_permissions)); + case MemoryPermission::Read: + case MemoryPermission::Write: + case MemoryPermission::ReadWrite: + case MemoryPermission::Execute: + case MemoryPermission::ReadExecute: + case MemoryPermission::WriteExecute: + case MemoryPermission::ReadWriteExecute: + case MemoryPermission::DontCare: + shared_memory->Map(addr, permissions_type, + static_cast<MemoryPermission>(other_permissions)); break; default: LOG_ERROR(Kernel_SVC, "unknown permissions=0x%08X", permissions); } - return 0; + return RESULT_SUCCESS; } /// Connect to an OS service given the port name, returns the handle to the port to out -static Result ConnectToPort(Handle* out, const char* port_name) { - Service::Interface* service = Service::g_manager->FetchFromPortName(port_name); +static ResultCode ConnectToPort(Handle* out_handle, const char* port_name) { + if (port_name == nullptr) + return ERR_NOT_FOUND; + if (std::strlen(port_name) > 11) + return ERR_PORT_NAME_TOO_LONG; LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); - _assert_msg_(KERNEL, (service != nullptr), "called, but service is not implemented!"); - *out = service->GetHandle(); + auto it = Service::g_kernel_named_ports.find(port_name); + if (it == Service::g_kernel_named_ports.end()) { + LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); + return ERR_NOT_FOUND; + } - return 0; + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(it->second)); + return RESULT_SUCCESS; } /// Synchronize to an OS service -static Result SendSyncRequest(Handle handle) { +static ResultCode SendSyncRequest(Handle handle) { SharedPtr<Kernel::Session> session = Kernel::g_handle_table.Get<Kernel::Session>(handle); if (session == nullptr) { - return InvalidHandle(ErrorModule::Kernel).raw; + return ERR_INVALID_HANDLE; } LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s)", handle, session->GetName().c_str()); - ResultVal<bool> wait = session->SyncRequest(); - if (wait.Succeeded() && *wait) { - Kernel::WaitCurrentThread(WAITTYPE_SYNCH); // TODO(bunnei): Is this correct? - } - - return wait.Code().raw; + return session->SyncRequest().Code(); } /// Close a handle -static Result CloseHandle(Handle handle) { - // ImplementMe - LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called handle=0x%08X", handle); - return 0; +static ResultCode CloseHandle(Handle handle) { + LOG_TRACE(Kernel_SVC, "Closing handle 0x%08X", handle); + return Kernel::g_handle_table.Close(handle); } /// Wait for a handle to synchronize, timeout after the specified nanoseconds -static Result WaitSynchronization1(Handle handle, s64 nano_seconds) { - // TODO(bunnei): Do something with nano_seconds, currently ignoring this - bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated - - SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handle); +static ResultCode WaitSynchronization1(Handle handle, s64 nano_seconds) { + auto object = Kernel::g_handle_table.GetWaitObject(handle); if (object == nullptr) - return InvalidHandle(ErrorModule::Kernel).raw; + return ERR_INVALID_HANDLE; LOG_TRACE(Kernel_SVC, "called handle=0x%08X(%s:%s), nanoseconds=%lld", handle, object->GetTypeName().c_str(), object->GetName().c_str(), nano_seconds); - ResultVal<bool> wait = object->WaitSynchronization(); - // Check for next thread to schedule - if (wait.Succeeded() && *wait) { + if (object->ShouldWait()) { + + object->AddWaitingThread(Kernel::GetCurrentThread()); + Kernel::WaitCurrentThread_WaitSynchronization(object, false, false); + + // Create an event to wake the thread up after the specified nanosecond delay has passed + Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); + HLE::Reschedule(__func__); + + // NOTE: output of this SVC will be set later depending on how the thread resumes + return RESULT_INVALID; } - return wait.Code().raw; + object->Acquire(); + + return RESULT_SUCCESS; } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static Result WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, - s64 nano_seconds) { - - // TODO(bunnei): Do something with nano_seconds, currently ignoring this - bool unlock_all = true; - bool wait_infinite = (nano_seconds == -1); // Used to wait until a thread has terminated - - LOG_TRACE(Kernel_SVC, "called handle_count=%d, wait_all=%s, nanoseconds=%lld", - handle_count, (wait_all ? "true" : "false"), nano_seconds); - - // Iterate through each handle, synchronize kernel object - for (s32 i = 0; i < handle_count; i++) { - SharedPtr<Kernel::Object> object = Kernel::g_handle_table.GetGeneric(handles[i]); - if (object == nullptr) - return InvalidHandle(ErrorModule::Kernel).raw; - - LOG_TRACE(Kernel_SVC, "\thandle[%d] = 0x%08X(%s:%s)", i, handles[i], - object->GetTypeName().c_str(), object->GetName().c_str()); - - // TODO(yuriks): Verify how the real function behaves when an error happens here - ResultVal<bool> wait_result = object->WaitSynchronization(); - bool wait = wait_result.Succeeded() && *wait_result; - - if (!wait && !wait_all) { - *out = i; - return RESULT_SUCCESS.raw; - } else { - unlock_all = false; +static ResultCode WaitSynchronizationN(s32* out, Handle* handles, s32 handle_count, bool wait_all, s64 nano_seconds) { + bool wait_thread = !wait_all; + int handle_index = 0; + + // Check if 'handles' is invalid + if (handles == nullptr) + return ResultCode(ErrorDescription::InvalidPointer, ErrorModule::Kernel, ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + + // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If + // this happens, the running application will crash. + _assert_msg_(Kernel, out != nullptr, "invalid output pointer specified!"); + + // Check if 'handle_count' is invalid + if (handle_count < 0) + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, ErrorSummary::InvalidArgument, ErrorLevel::Usage); + + // If 'handle_count' is non-zero, iterate through each handle and wait the current thread if + // necessary + if (handle_count != 0) { + bool selected = false; // True once an object has been selected + for (int i = 0; i < handle_count; ++i) { + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + if (object == nullptr) + return ERR_INVALID_HANDLE; + + // Check if the current thread should wait on this object... + if (object->ShouldWait()) { + + // Check we are waiting on all objects... + if (wait_all) + // Wait the thread + wait_thread = true; + } else { + // Do not wait on this object, check if this object should be selected... + if (!wait_all && !selected) { + // Do not wait the thread + wait_thread = false; + handle_index = i; + selected = true; + } + } + } + } else { + // If no handles were passed in, put the thread to sleep only when 'wait_all' is false + // NOTE: This should deadlock the current thread if no timeout was specified + if (!wait_all) { + wait_thread = true; + Kernel::WaitCurrentThread_WaitSynchronization(nullptr, true, wait_all); } } - if (wait_all && unlock_all) { - *out = handle_count; - return RESULT_SUCCESS.raw; + // If thread should wait, then set its state to waiting and then reschedule... + if (wait_thread) { + + // Actually wait the current thread on each object if we decided to wait... + for (int i = 0; i < handle_count; ++i) { + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + object->AddWaitingThread(Kernel::GetCurrentThread()); + Kernel::WaitCurrentThread_WaitSynchronization(object, true, wait_all); + } + + // Create an event to wake the thread up after the specified nanosecond delay has passed + Kernel::GetCurrentThread()->WakeAfterDelay(nano_seconds); + + HLE::Reschedule(__func__); + + // NOTE: output of this SVC will be set later depending on how the thread resumes + return RESULT_INVALID; } - // Check for next thread to schedule - HLE::Reschedule(__func__); + // Acquire objects if we did not wait... + for (int i = 0; i < handle_count; ++i) { + auto object = Kernel::g_handle_table.GetWaitObject(handles[i]); + + // Acquire the object if it is not waiting... + if (!object->ShouldWait()) { + object->Acquire(); - return RESULT_SUCCESS.raw; + // If this was the first non-waiting object and 'wait_all' is false, don't acquire + // any other objects + if (!wait_all) + break; + } + } + + // TODO(bunnei): If 'wait_all' is true, this is probably wrong. However, real hardware does + // not seem to set it to any meaningful value. + *out = wait_all ? 0 : handle_index; + + return RESULT_SUCCESS; } /// Create an address arbiter (to allocate access to shared resources) -static Result CreateAddressArbiter(u32* arbiter) { - Handle handle = Kernel::CreateAddressArbiter(); - *arbiter = handle; - return 0; +static ResultCode CreateAddressArbiter(Handle* out_handle) { + using Kernel::AddressArbiter; + + SharedPtr<AddressArbiter> arbiter = AddressArbiter::Create(); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(arbiter))); + LOG_TRACE(Kernel_SVC, "returned handle=0x%08X", *out_handle); + return RESULT_SUCCESS; } /// Arbitrate address -static Result ArbitrateAddress(Handle arbiter, u32 address, u32 type, u32 value, s64 nanoseconds) { - LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", arbiter, +static ResultCode ArbitrateAddress(Handle handle, u32 address, u32 type, u32 value, s64 nanoseconds) { + using Kernel::AddressArbiter; + + LOG_TRACE(Kernel_SVC, "called handle=0x%08X, address=0x%08X, type=0x%08X, value=0x%08X", handle, address, type, value); - return Kernel::ArbitrateAddress(arbiter, static_cast<Kernel::ArbitrationType>(type), - address, value).raw; + + SharedPtr<AddressArbiter> arbiter = Kernel::g_handle_table.Get<AddressArbiter>(handle); + if (arbiter == nullptr) + return ERR_INVALID_HANDLE; + + return arbiter->ArbitrateAddress(static_cast<Kernel::ArbitrationType>(type), + address, value, nanoseconds); } /// Used to output a message on a debug hardware unit - does nothing on a retail unit @@ -204,26 +288,26 @@ static void OutputDebugString(const char* string) { } /// Get resource limit -static Result GetResourceLimit(Handle* resource_limit, Handle process) { +static ResultCode GetResourceLimit(Handle* resource_limit, Handle process) { // With regards to proceess values: // 0xFFFF8001 is a handle alias for the current KProcess, and 0xFFFF8000 is a handle alias for // the current KThread. *resource_limit = 0xDEADBEEF; LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called process=0x%08X", process); - return 0; + return RESULT_SUCCESS; } /// Get resource limit current values -static Result GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, +static ResultCode GetResourceLimitCurrentValues(s64* values, Handle resource_limit, void* names, s32 name_count) { LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called resource_limit=%08X, names=%s, name_count=%d", resource_limit, names, name_count); Memory::Write32(Core::g_app_core->GetReg(0), 0); // Normmatt: Set used memory to 0 for now - return 0; + return RESULT_SUCCESS; } /// Creates a new thread -static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { +static ResultCode CreateThread(u32* out_handle, u32 priority, u32 entry_point, u32 arg, u32 stack_top, u32 processor_id) { using Kernel::Thread; std::string name; @@ -234,25 +318,20 @@ static Result CreateThread(u32 priority, u32 entry_point, u32 arg, u32 stack_top name = Common::StringFromFormat("unknown-%08x", entry_point); } - ResultVal<SharedPtr<Thread>> thread_res = Kernel::Thread::Create( - name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE); - if (thread_res.Failed()) - return thread_res.Code().raw; - SharedPtr<Thread> thread = std::move(*thread_res); - - // TODO(yuriks): Create new handle instead of using built-in - Core::g_app_core->SetReg(1, thread->GetHandle()); + CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create( + name, entry_point, priority, arg, processor_id, stack_top, Kernel::DEFAULT_STACK_SIZE)); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(thread))); LOG_TRACE(Kernel_SVC, "called entrypoint=0x%08X (%s), arg=0x%08X, stacktop=0x%08X, " "threadpriority=0x%08X, processorid=0x%08X : created handle=0x%08X", entry_point, - name.c_str(), arg, stack_top, priority, processor_id, thread->GetHandle()); + name.c_str(), arg, stack_top, priority, processor_id, *out_handle); if (THREADPROCESSORID_1 == processor_id) { LOG_WARNING(Kernel_SVC, "thread designated for system CPU core (UNIMPLEMENTED) will be run with app core scheduling"); } - return 0; + return RESULT_SUCCESS; } /// Called when a thread exits @@ -264,127 +343,193 @@ static void ExitThread() { } /// Gets the priority for the specified thread -static Result GetThreadPriority(s32* priority, Handle handle) { +static ResultCode GetThreadPriority(s32* priority, Handle handle) { const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) - return InvalidHandle(ErrorModule::Kernel).raw; + return ERR_INVALID_HANDLE; *priority = thread->GetPriority(); - return RESULT_SUCCESS.raw; + return RESULT_SUCCESS; } /// Sets the priority for the specified thread -static Result SetThreadPriority(Handle handle, s32 priority) { +static ResultCode SetThreadPriority(Handle handle, s32 priority) { SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) - return InvalidHandle(ErrorModule::Kernel).raw; + return ERR_INVALID_HANDLE; thread->SetPriority(priority); - return RESULT_SUCCESS.raw; + return RESULT_SUCCESS; } /// Create a mutex -static Result CreateMutex(Handle* mutex, u32 initial_locked) { - *mutex = Kernel::CreateMutex((initial_locked != 0)); +static ResultCode CreateMutex(Handle* out_handle, u32 initial_locked) { + using Kernel::Mutex; + + SharedPtr<Mutex> mutex = Mutex::Create(initial_locked != 0); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(mutex))); + LOG_TRACE(Kernel_SVC, "called initial_locked=%s : created handle=0x%08X", - initial_locked ? "true" : "false", *mutex); - return 0; + initial_locked ? "true" : "false", *out_handle); + return RESULT_SUCCESS; } /// Release a mutex -static Result ReleaseMutex(Handle handle) { +static ResultCode ReleaseMutex(Handle handle) { + using Kernel::Mutex; + LOG_TRACE(Kernel_SVC, "called handle=0x%08X", handle); - ResultCode res = Kernel::ReleaseMutex(handle); - return res.raw; + + SharedPtr<Mutex> mutex = Kernel::g_handle_table.Get<Mutex>(handle); + if (mutex == nullptr) + return ERR_INVALID_HANDLE; + + mutex->Release(); + return RESULT_SUCCESS; } /// Get the ID for the specified thread. -static Result GetThreadId(u32* thread_id, Handle handle) { +static ResultCode GetThreadId(u32* thread_id, Handle handle) { LOG_TRACE(Kernel_SVC, "called thread=0x%08X", handle); const SharedPtr<Kernel::Thread> thread = Kernel::g_handle_table.Get<Kernel::Thread>(handle); if (thread == nullptr) - return InvalidHandle(ErrorModule::Kernel).raw; + return ERR_INVALID_HANDLE; *thread_id = thread->GetThreadId(); - return RESULT_SUCCESS.raw; + return RESULT_SUCCESS; } /// Creates a semaphore -static Result CreateSemaphore(Handle* semaphore, s32 initial_count, s32 max_count) { - ResultCode res = Kernel::CreateSemaphore(semaphore, initial_count, max_count); +static ResultCode CreateSemaphore(Handle* out_handle, s32 initial_count, s32 max_count) { + using Kernel::Semaphore; + + CASCADE_RESULT(SharedPtr<Semaphore> semaphore, Semaphore::Create(initial_count, max_count)); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(semaphore))); + LOG_TRACE(Kernel_SVC, "called initial_count=%d, max_count=%d, created handle=0x%08X", - initial_count, max_count, *semaphore); - return res.raw; + initial_count, max_count, *out_handle); + return RESULT_SUCCESS; } /// Releases a certain number of slots in a semaphore -static Result ReleaseSemaphore(s32* count, Handle semaphore, s32 release_count) { - LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, semaphore); - ResultCode res = Kernel::ReleaseSemaphore(count, semaphore, release_count); - return res.raw; +static ResultCode ReleaseSemaphore(s32* count, Handle handle, s32 release_count) { + using Kernel::Semaphore; + + LOG_TRACE(Kernel_SVC, "called release_count=%d, handle=0x%08X", release_count, handle); + + SharedPtr<Semaphore> semaphore = Kernel::g_handle_table.Get<Semaphore>(handle); + if (semaphore == nullptr) + return ERR_INVALID_HANDLE; + + CASCADE_RESULT(*count, semaphore->Release(release_count)); + return RESULT_SUCCESS; } /// Query memory -static Result QueryMemory(void* info, void* out, u32 addr) { +static ResultCode QueryMemory(void* info, void* out, u32 addr) { LOG_ERROR(Kernel_SVC, "(UNIMPLEMENTED) called addr=0x%08X", addr); - return 0; + return RESULT_SUCCESS; } /// Create an event -static Result CreateEvent(Handle* evt, u32 reset_type) { - *evt = Kernel::CreateEvent((ResetType)reset_type); +static ResultCode CreateEvent(Handle* out_handle, u32 reset_type) { + using Kernel::Event; + + SharedPtr<Event> evt = Kernel::Event::Create(static_cast<ResetType>(reset_type)); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(evt))); + LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", - reset_type, *evt); - return 0; + reset_type, *out_handle); + return RESULT_SUCCESS; } /// Duplicates a kernel handle -static Result DuplicateHandle(Handle* out, Handle handle) { - ResultVal<Handle> out_h = Kernel::g_handle_table.Duplicate(handle); - if (out_h.Succeeded()) { - *out = *out_h; - LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out); - } - return out_h.Code().raw; +static ResultCode DuplicateHandle(Handle* out, Handle handle) { + CASCADE_RESULT(*out, Kernel::g_handle_table.Duplicate(handle)); + LOG_TRACE(Kernel_SVC, "duplicated 0x%08X to 0x%08X", handle, *out); + return RESULT_SUCCESS; } /// Signals an event -static Result SignalEvent(Handle evt) { - LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); - return Kernel::SignalEvent(evt).raw; +static ResultCode SignalEvent(Handle handle) { + using Kernel::Event; + LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle); + + SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle); + if (evt == nullptr) + return ERR_INVALID_HANDLE; + + evt->Signal(); + HLE::Reschedule(__func__); + return RESULT_SUCCESS; } /// Clears an event -static Result ClearEvent(Handle evt) { - LOG_TRACE(Kernel_SVC, "called event=0x%08X", evt); - return Kernel::ClearEvent(evt).raw; +static ResultCode ClearEvent(Handle handle) { + using Kernel::Event; + LOG_TRACE(Kernel_SVC, "called event=0x%08X", handle); + + SharedPtr<Event> evt = Kernel::g_handle_table.Get<Kernel::Event>(handle); + if (evt == nullptr) + return ERR_INVALID_HANDLE; + + evt->Clear(); + return RESULT_SUCCESS; } /// Creates a timer -static Result CreateTimer(Handle* handle, u32 reset_type) { - ResultCode res = Kernel::CreateTimer(handle, static_cast<ResetType>(reset_type)); +static ResultCode CreateTimer(Handle* out_handle, u32 reset_type) { + using Kernel::Timer; + + SharedPtr<Timer> timer = Timer::Create(static_cast<ResetType>(reset_type)); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(timer))); + LOG_TRACE(Kernel_SVC, "called reset_type=0x%08X : created handle=0x%08X", - reset_type, *handle); - return res.raw; + reset_type, *out_handle); + return RESULT_SUCCESS; } /// Clears a timer -static Result ClearTimer(Handle handle) { +static ResultCode ClearTimer(Handle handle) { + using Kernel::Timer; + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); - return Kernel::ClearTimer(handle).raw; + + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); + if (timer == nullptr) + return ERR_INVALID_HANDLE; + + timer->Clear(); + return RESULT_SUCCESS; } /// Starts a timer -static Result SetTimer(Handle handle, s64 initial, s64 interval) { +static ResultCode SetTimer(Handle handle, s64 initial, s64 interval) { + using Kernel::Timer; + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); - return Kernel::SetTimer(handle, initial, interval).raw; + + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); + if (timer == nullptr) + return ERR_INVALID_HANDLE; + + timer->Set(initial, interval); + return RESULT_SUCCESS; } /// Cancels a timer -static Result CancelTimer(Handle handle) { +static ResultCode CancelTimer(Handle handle) { + using Kernel::Timer; + LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); - return Kernel::CancelTimer(handle).raw; + + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); + if (timer == nullptr) + return ERR_INVALID_HANDLE; + + timer->Cancel(); + return RESULT_SUCCESS; } /// Sleep the current thread @@ -392,10 +537,10 @@ static void SleepThread(s64 nanoseconds) { LOG_TRACE(Kernel_SVC, "called nanoseconds=%lld", nanoseconds); // Sleep current thread and check for next thread to schedule - Kernel::WaitCurrentThread(WAITTYPE_SLEEP); + Kernel::WaitCurrentThread_Sleep(); // Create an event to wake the thread up after the specified nanosecond delay has passed - Kernel::WakeThreadAfterDelay(Kernel::GetCurrentThread(), nanoseconds); + Kernel::GetCurrentThread()->WakeAfterDelay(nanoseconds); HLE::Reschedule(__func__); } @@ -406,15 +551,16 @@ static s64 GetSystemTick() { } /// Creates a memory block at the specified address with the specified permissions and size -static Result CreateMemoryBlock(Handle* memblock, u32 addr, u32 size, u32 my_permission, - u32 other_permission) { - +static ResultCode CreateMemoryBlock(Handle* out_handle, u32 addr, u32 size, u32 my_permission, + u32 other_permission) { + using Kernel::SharedMemory; // TODO(Subv): Implement this function - Handle shared_memory = Kernel::CreateSharedMemory(); - *memblock = shared_memory; + SharedPtr<SharedMemory> shared_memory = SharedMemory::Create(); + CASCADE_RESULT(*out_handle, Kernel::g_handle_table.Create(std::move(shared_memory))); + LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x%08X", addr); - return 0; + return RESULT_SUCCESS; } const HLE::FunctionDef SVC_Table[] = { @@ -533,15 +679,15 @@ const HLE::FunctionDef SVC_Table[] = { {0x70, nullptr, "ControlProcessMemory"}, {0x71, nullptr, "MapProcessMemory"}, {0x72, nullptr, "UnmapProcessMemory"}, - {0x73, nullptr, "Unknown"}, - {0x74, nullptr, "Unknown"}, - {0x75, nullptr, "Unknown"}, + {0x73, nullptr, "CreateCodeSet"}, + {0x74, nullptr, "RandomStub"}, + {0x75, nullptr, "CreateProcess"}, {0x76, nullptr, "TerminateProcess"}, - {0x77, nullptr, "Unknown"}, + {0x77, nullptr, "SetProcessResourceLimits"}, {0x78, nullptr, "CreateResourceLimit"}, - {0x79, nullptr, "Unknown"}, - {0x7A, nullptr, "Unknown"}, - {0x7B, nullptr, "Unknown"}, + {0x79, nullptr, "SetResourceLimitValues"}, + {0x7A, nullptr, "AddCodeSegment"}, + {0x7B, nullptr, "Backdoor"}, {0x7C, nullptr, "KernelSetState"}, {0x7D, nullptr, "QueryProcessMemory"}, }; |
