diff options
Diffstat (limited to 'src/core/hle')
44 files changed, 1752 insertions, 611 deletions
diff --git a/src/core/hle/applets/applet.cpp b/src/core/hle/applets/applet.cpp index 645b2d5fe..9c43ed2fd 100644 --- a/src/core/hle/applets/applet.cpp +++ b/src/core/hle/applets/applet.cpp @@ -12,6 +12,7 @@ #include "core/hle/applets/applet.h" #include "core/hle/applets/erreula.h" #include "core/hle/applets/mii_selector.h" +#include "core/hle/applets/mint.h" #include "core/hle/applets/swkbd.h" #include "core/hle/result.h" #include "core/hle/service/apt/apt.h" @@ -56,6 +57,10 @@ ResultCode Applet::Create(Service::APT::AppletId id) { case Service::APT::AppletId::Error2: applets[id] = std::make_shared<ErrEula>(id); break; + case Service::APT::AppletId::Mint: + case Service::APT::AppletId::Mint2: + applets[id] = std::make_shared<Mint>(id); + break; default: LOG_ERROR(Service_APT, "Could not create applet %u", id); // TODO(Subv): Find the right error code diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp new file mode 100644 index 000000000..31a79ea17 --- /dev/null +++ b/src/core/hle/applets/mint.cpp @@ -0,0 +1,72 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/string_util.h" +#include "core/hle/applets/mint.h" +#include "core/hle/service/apt/apt.h" + +namespace HLE { +namespace Applets { + +ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& parameter) { + if (parameter.signal != static_cast<u32>(Service::APT::SignalType::Request)) { + LOG_ERROR(Service_APT, "unsupported signal %u", parameter.signal); + UNIMPLEMENTED(); + // TODO(Subv): Find the right error code + return ResultCode(-1); + } + + // The Request message contains a buffer with the size of the framebuffer shared + // memory. + // Create the SharedMemory that will hold the framebuffer data + Service::APT::CaptureBufferInfo capture_info; + ASSERT(sizeof(capture_info) == parameter.buffer.size()); + + memcpy(&capture_info, parameter.buffer.data(), sizeof(capture_info)); + + // TODO: allocated memory never released + using Kernel::MemoryPermission; + // Allocate a heap block of the required size for this applet. + heap_memory = std::make_shared<std::vector<u8>>(capture_info.size); + // Create a SharedMemory that directly points to this heap block. + framebuffer_memory = Kernel::SharedMemory::CreateForApplet( + heap_memory, 0, heap_memory->size(), MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, "Mint Memory"); + + // Send the response message with the newly created SharedMemory + Service::APT::MessageParameter result; + result.signal = static_cast<u32>(Service::APT::SignalType::Response); + result.buffer.clear(); + result.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + result.sender_id = static_cast<u32>(id); + result.object = framebuffer_memory; + + Service::APT::SendParameter(result); + return RESULT_SUCCESS; +} + +ResultCode Mint::StartImpl(const Service::APT::AppletStartupParameter& parameter) { + is_running = true; + + // TODO(Subv): Set the expected fields in the response buffer before resending it to the + // application. + // TODO(Subv): Reverse the parameter format for the Mint applet + + // Let the application know that we're closing + Service::APT::MessageParameter message; + message.buffer.resize(parameter.buffer.size()); + std::fill(message.buffer.begin(), message.buffer.end(), 0); + message.signal = static_cast<u32>(Service::APT::SignalType::WakeupByExit); + message.destination_id = static_cast<u32>(Service::APT::AppletId::Application); + message.sender_id = static_cast<u32>(id); + Service::APT::SendParameter(message); + + is_running = false; + return RESULT_SUCCESS; +} + +void Mint::Update() {} + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/applets/mint.h b/src/core/hle/applets/mint.h new file mode 100644 index 000000000..d23dc40f9 --- /dev/null +++ b/src/core/hle/applets/mint.h @@ -0,0 +1,29 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/applets/applet.h" +#include "core/hle/kernel/shared_memory.h" + +namespace HLE { +namespace Applets { + +class Mint final : public Applet { +public: + explicit Mint(Service::APT::AppletId id) : Applet(id) {} + + ResultCode ReceiveParameter(const Service::APT::MessageParameter& parameter) override; + ResultCode StartImpl(const Service::APT::AppletStartupParameter& parameter) override; + void Update() override; + +private: + /// This SharedMemory will be created when we receive the Request message. + /// It holds the framebuffer info retrieved by the application with + /// GSPGPU::ImportDisplayCaptureInfo + Kernel::SharedPtr<Kernel::SharedMemory> framebuffer_memory; +}; + +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/config_mem.cpp b/src/core/hle/config_mem.cpp index ccd73cfcb..e386ccdc6 100644 --- a/src/core/hle/config_mem.cpp +++ b/src/core/hle/config_mem.cpp @@ -14,15 +14,18 @@ ConfigMemDef config_mem; void Init() { std::memset(&config_mem, 0, sizeof(config_mem)); - config_mem.update_flag = 0; // No update + // Values extracted from firmware 11.2.0-35E + config_mem.kernel_version_min = 0x34; + config_mem.kernel_version_maj = 0x2; + config_mem.ns_tid = 0x0004013000008002; config_mem.sys_core_ver = 0x2; config_mem.unit_info = 0x1; // Bit 0 set for Retail - config_mem.prev_firm = 0; - config_mem.firm_unk = 0; - config_mem.firm_version_rev = 0; - config_mem.firm_version_min = 0x40; + config_mem.prev_firm = 0x1; + config_mem.ctr_sdk_ver = 0x0000F297; + config_mem.firm_version_min = 0x34; config_mem.firm_version_maj = 0x2; config_mem.firm_sys_core_ver = 0x2; + config_mem.firm_ctr_sdk_ver = 0x0000F297; } } // namespace diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 7875971ce..f6eb900f0 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -256,9 +256,9 @@ void Wrap() { func(((s64)PARAM(1) << 32) | PARAM(0)); } -template <void func(const char*)> +template <void func(const char*, int len)> void Wrap() { - func((char*)Memory::GetPointer(PARAM(0))); + func((char*)Memory::GetPointer(PARAM(0)), PARAM(1)); } template <void func(u8)> diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index 4e094faa7..3a5d481a5 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -10,7 +10,8 @@ namespace Kernel { -static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of header +/// Offset into command buffer of header +static const int kCommandHeaderOffset = 0x80; /** * Returns a pointer to the command buffer in the current thread's TLS @@ -18,12 +19,26 @@ static const int kCommandHeaderOffset = 0x80; ///< Offset into command buffer of * the thread's TLS to an intermediate buffer in kernel memory, and then copied again to * the service handler process' memory. * @param offset Optional offset into command buffer + * @param offset Optional offset into command buffer (in bytes) * @return Pointer to command buffer */ inline u32* GetCommandBuffer(const int offset = 0) { return (u32*)Memory::GetPointer(GetCurrentThread()->GetTLSAddress() + kCommandHeaderOffset + offset); } + +/// Offset into static buffers, relative to command buffer header +static const int kStaticBuffersOffset = 0x100; + +/** + * Returns a pointer to the static buffers area in the current thread's TLS + * TODO(Subv): cf. GetCommandBuffer + * @param offset Optional offset into static buffers area (in bytes) + * @return Pointer to static buffers area + */ +inline u32* GetStaticBuffers(const int offset = 0) { + return GetCommandBuffer(kStaticBuffersOffset + offset); +} } namespace IPC { @@ -40,10 +55,17 @@ enum DescriptorType : u32 { CallingPid = 0x20, }; +union Header { + u32 raw; + BitField<0, 6, u32> translate_params_size; + BitField<6, 6, u32> normal_params_size; + BitField<16, 16, u32> command_id; +}; + /** * @brief Creates a command header to be used for IPC * @param command_id ID of the command to create a header for. - * @param normal_params Size of the normal parameters in words. Up to 63. + * @param normal_params_size Size of the normal parameters in words. Up to 63. * @param translate_params_size Size of the translate parameters in words. Up to 63. * @return The created IPC header. * @@ -51,24 +73,16 @@ enum DescriptorType : u32 { * through modifications and checks by the kernel. * The translate parameters are described by headers generated with the IPC::*Desc functions. * - * @note While #normal_params is equivalent to the number of normal parameters, - * #translate_params_size includes the size occupied by the translate parameters headers. + * @note While @p normal_params_size is equivalent to the number of normal parameters, + * @p translate_params_size includes the size occupied by the translate parameters headers. */ -constexpr u32 MakeHeader(u16 command_id, unsigned int normal_params, - unsigned int translate_params_size) { - return (u32(command_id) << 16) | ((u32(normal_params) & 0x3F) << 6) | - (u32(translate_params_size) & 0x3F); -} - -union Header { - u32 raw; - BitField<0, 6, u32> translate_params_size; - BitField<6, 6, u32> normal_params; - BitField<16, 16, u32> command_id; -}; - -inline Header ParseHeader(u32 header) { - return {header}; +inline u32 MakeHeader(u16 command_id, unsigned int normal_params_size, + unsigned int translate_params_size) { + Header header{}; + header.command_id.Assign(command_id); + header.normal_params_size.Assign(normal_params_size); + header.translate_params_size.Assign(translate_params_size); + return header.raw; } constexpr u32 MoveHandleDesc(u32 num_handles = 1) { @@ -83,7 +97,7 @@ constexpr u32 CallingPidDesc() { return CallingPid; } -constexpr bool isHandleDescriptor(u32 descriptor) { +constexpr bool IsHandleDescriptor(u32 descriptor) { return (descriptor & 0xF) == 0x0; } @@ -91,18 +105,19 @@ constexpr u32 HandleNumberFromDesc(u32 handle_descriptor) { return (handle_descriptor >> 26) + 1; } -constexpr u32 StaticBufferDesc(u32 size, u8 buffer_id) { - return StaticBuffer | (size << 14) | ((buffer_id & 0xF) << 10); -} - union StaticBufferDescInfo { u32 raw; + BitField<0, 4, u32> descriptor_type; BitField<10, 4, u32> buffer_id; BitField<14, 18, u32> size; }; -inline StaticBufferDescInfo ParseStaticBufferDesc(const u32 desc) { - return {desc}; +inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { + StaticBufferDescInfo info{}; + info.descriptor_type.Assign(StaticBuffer); + info.buffer_id.Assign(buffer_id); + info.size.Assign(size); + return info.raw; } /** @@ -122,29 +137,30 @@ inline u32 PXIBufferDesc(u32 size, unsigned buffer_id, bool is_read_only) { return type | (size << 8) | ((buffer_id & 0xF) << 4); } -enum MappedBufferPermissions { +enum MappedBufferPermissions : u32 { R = 1, W = 2, RW = R | W, }; -constexpr u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { - return MappedBuffer | (size << 4) | (u32(perms) << 1); -} - union MappedBufferDescInfo { u32 raw; - BitField<4, 28, u32> size; + BitField<0, 4, u32> flags; BitField<1, 2, MappedBufferPermissions> perms; + BitField<4, 28, u32> size; }; -inline MappedBufferDescInfo ParseMappedBufferDesc(const u32 desc) { - return {desc}; +inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { + MappedBufferDescInfo info{}; + info.flags.Assign(MappedBuffer); + info.perms.Assign(perms); + info.size.Assign(size); + return info.raw; } inline DescriptorType GetDescriptorType(u32 descriptor) { // Note: Those checks must be done in this order - if (isHandleDescriptor(descriptor)) + if (IsHandleDescriptor(descriptor)) return (DescriptorType)(descriptor & 0x30); // handle the fact that the following descriptors can have rights diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h new file mode 100644 index 000000000..06c4c5a85 --- /dev/null +++ b/src/core/hle/ipc_helpers.h @@ -0,0 +1,353 @@ +// Copyright 2016 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once +#include "core/hle/ipc.h" +#include "core/hle/kernel/kernel.h" + +namespace IPC { + +class RequestHelperBase { +protected: + u32* cmdbuf; + ptrdiff_t index = 1; + Header header; + +public: + RequestHelperBase(u32* command_buffer, Header command_header) + : cmdbuf(command_buffer), header(command_header) {} + + /// Returns the total size of the request in words + size_t TotalSize() const { + return 1 /* command header */ + header.normal_params_size + header.translate_params_size; + } + + void ValidateHeader() { + DEBUG_ASSERT_MSG(index == TotalSize(), "Operations do not match the header (cmd 0x%x)", + header.raw); + } + + void Skip(unsigned size_in_words, bool set_to_null) { + if (set_to_null) + memset(cmdbuf + index, 0, size_in_words * sizeof(u32)); + index += size_in_words; + } + + /** + * @brief Retrieves the address of a static buffer, used when a buffer is needed for output + * @param buffer_id The index of the static buffer + * @param data_size If non-null, will store the size of the buffer + */ + VAddr PeekStaticBuffer(u8 buffer_id, size_t* data_size = nullptr) const { + u32* static_buffer = cmdbuf + Kernel::kStaticBuffersOffset / sizeof(u32) + buffer_id * 2; + if (data_size) + *data_size = StaticBufferDescInfo{static_buffer[0]}.size; + return static_buffer[1]; + } +}; + +class RequestBuilder : public RequestHelperBase { +public: + RequestBuilder(u32* command_buffer, Header command_header) + : RequestHelperBase(command_buffer, command_header) { + cmdbuf[0] = header.raw; + } + explicit RequestBuilder(u32* command_buffer, u32 command_header) + : RequestBuilder(command_buffer, Header{command_header}) {} + RequestBuilder(u32* command_buffer, u16 command_id, unsigned normal_params_size, + unsigned translate_params_size) + : RequestBuilder(command_buffer, + MakeHeader(command_id, normal_params_size, translate_params_size)) {} + + // Validate on destruction, as there shouldn't be any case where we don't want it + ~RequestBuilder() { + ValidateHeader(); + } + + template <typename T> + void Push(T value); + + template <typename First, typename... Other> + void Push(const First& first_value, const Other&... other_values); + + /** + * @brief Copies the content of the given trivially copyable class to the buffer as a normal + * param + * @note: The input class must be correctly packed/padded to fit hardware layout. + */ + template <typename T> + void PushRaw(const T& value); + + // TODO : ensure that translate params are added after all regular params + template <typename... H> + void PushCopyHandles(H... handles); + + template <typename... H> + void PushMoveHandles(H... handles); + + void PushCurrentPIDHandle(); + + void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); + + void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms); +}; + +/// Push /// + +template <> +inline void RequestBuilder::Push(u32 value) { + cmdbuf[index++] = value; +} + +template <typename T> +void RequestBuilder::PushRaw(const T& value) { + static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); + std::memcpy(cmdbuf + index, &value, sizeof(T)); + index += (sizeof(T) + 3) / 4; // round up to word length +} + +template <> +inline void RequestBuilder::Push(u8 value) { + PushRaw(value); +} + +template <> +inline void RequestBuilder::Push(u16 value) { + PushRaw(value); +} + +template <> +inline void RequestBuilder::Push(u64 value) { + Push(static_cast<u32>(value)); + Push(static_cast<u32>(value >> 32)); +} + +template <> +inline void RequestBuilder::Push(bool value) { + Push(static_cast<u8>(value)); +} + +template <> +inline void RequestBuilder::Push(ResultCode value) { + Push(value.raw); +} + +template <typename First, typename... Other> +void RequestBuilder::Push(const First& first_value, const Other&... other_values) { + Push(first_value); + Push(other_values...); +} + +template <typename... H> +inline void RequestBuilder::PushCopyHandles(H... handles) { + Push(CopyHandleDesc(sizeof...(H))); + Push(static_cast<Kernel::Handle>(handles)...); +} + +template <typename... H> +inline void RequestBuilder::PushMoveHandles(H... handles) { + Push(MoveHandleDesc(sizeof...(H))); + Push(static_cast<Kernel::Handle>(handles)...); +} + +inline void RequestBuilder::PushCurrentPIDHandle() { + Push(CallingPidDesc()); + Push(u32(0)); +} + +inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { + Push(StaticBufferDesc(size, buffer_id)); + Push(buffer_vaddr); +} + +inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, + MappedBufferPermissions perms) { + Push(MappedBufferDesc(size, perms)); + Push(buffer_vaddr); +} + +class RequestParser : public RequestHelperBase { +public: + RequestParser(u32* command_buffer, Header command_header) + : RequestHelperBase(command_buffer, command_header) {} + explicit RequestParser(u32* command_buffer, u32 command_header) + : RequestParser(command_buffer, Header{command_header}) {} + RequestParser(u32* command_buffer, u16 command_id, unsigned normal_params_size, + unsigned translate_params_size) + : RequestParser(command_buffer, + MakeHeader(command_id, normal_params_size, translate_params_size)) {} + + RequestBuilder MakeBuilder(u32 normal_params_size, u32 translate_params_size, + bool validateHeader = true) { + if (validateHeader) + ValidateHeader(); + Header builderHeader{ + MakeHeader(header.command_id, normal_params_size, translate_params_size)}; + return {cmdbuf, builderHeader}; + } + + template <typename T> + T Pop(); + + template <typename T> + void Pop(T& value); + + template <typename First, typename... Other> + void Pop(First& first_value, Other&... other_values); + + Kernel::Handle PopHandle(); + + template <typename... H> + void PopHandles(H&... handles); + + /** + * @brief Pops the static buffer vaddr + * @return The virtual address of the buffer + * @param[out] data_size If non-null, the pointed value will be set to the size of the data + * @param[out] useStaticBuffersToGetVaddr Indicates if we should read the vaddr from the static + * buffers (which is the correct thing to do, but no service presently implement it) instead of + * using the same value as the process who sent the request + * given by the source process + * + * Static buffers must be set up before any IPC request using those is sent. + * It is the duty of the process (usually services) to allocate and set up the receiving static + * buffer information + * Please note that the setup uses virtual addresses. + */ + VAddr PopStaticBuffer(size_t* data_size = nullptr, bool useStaticBuffersToGetVaddr = false); + + /** + * @brief Pops the mapped buffer vaddr + * @return The virtual address of the buffer + * @param[out] data_size If non-null, the pointed value will be set to the size of the data + * given by the source process + * @param[out] buffer_perms If non-null, the pointed value will be set to the permissions of the + * buffer + */ + VAddr PopMappedBuffer(size_t* data_size = nullptr, + MappedBufferPermissions* buffer_perms = nullptr); + + /** + * @brief Reads the next normal parameters as a struct, by copying it + * @note: The output class must be correctly packed/padded to fit hardware layout. + */ + template <typename T> + void PopRaw(T& value); + + /** + * @brief Reads the next normal parameters as a struct, by copying it into a new value + * @note: The output class must be correctly packed/padded to fit hardware layout. + */ + template <typename T> + T PopRaw(); +}; + +/// Pop /// + +template <> +inline u32 RequestParser::Pop() { + return cmdbuf[index++]; +} + +template <typename T> +void RequestParser::PopRaw(T& value) { + static_assert(std::is_trivially_copyable<T>(), "Raw types should be trivially copyable"); + std::memcpy(&value, cmdbuf + index, sizeof(T)); + index += (sizeof(T) + 3) / 4; // round up to word length +} + +template <typename T> +T RequestParser::PopRaw() { + T value; + PopRaw(value); + return value; +} + +template <> +inline u8 RequestParser::Pop() { + return PopRaw<u8>(); +} + +template <> +inline u16 RequestParser::Pop() { + return PopRaw<u16>(); +} + +template <> +inline u64 RequestParser::Pop() { + const u64 lsw = Pop<u32>(); + const u64 msw = Pop<u32>(); + return msw << 32 | lsw; +} + +template <> +inline bool RequestParser::Pop() { + return Pop<u8>() != 0; +} + +template <> +inline ResultCode RequestParser::Pop() { + return ResultCode{Pop<u32>()}; +} + +template <typename T> +void RequestParser::Pop(T& value) { + value = Pop<T>(); +} + +template <typename First, typename... Other> +void RequestParser::Pop(First& first_value, Other&... other_values) { + first_value = Pop<First>(); + Pop(other_values...); +} + +inline Kernel::Handle RequestParser::PopHandle() { + const u32 handle_descriptor = Pop<u32>(); + DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), + "Tried to pop handle(s) but the descriptor is not a handle descriptor"); + DEBUG_ASSERT_MSG(HandleNumberFromDesc(handle_descriptor) == 1, + "Descriptor indicates that there isn't exactly one handle"); + return Pop<Kernel::Handle>(); +} + +template <typename... H> +void RequestParser::PopHandles(H&... handles) { + const u32 handle_descriptor = Pop<u32>(); + const int handles_number = sizeof...(H); + DEBUG_ASSERT_MSG(IsHandleDescriptor(handle_descriptor), + "Tried to pop handle(s) but the descriptor is not a handle descriptor"); + DEBUG_ASSERT_MSG(handles_number == HandleNumberFromDesc(handle_descriptor), + "Number of handles doesn't match the descriptor"); + Pop(static_cast<Kernel::Handle&>(handles)...); +} + +inline VAddr RequestParser::PopStaticBuffer(size_t* data_size, bool useStaticBuffersToGetVaddr) { + const u32 sbuffer_descriptor = Pop<u32>(); + StaticBufferDescInfo bufferInfo{sbuffer_descriptor}; + if (data_size != nullptr) + *data_size = bufferInfo.size; + if (!useStaticBuffersToGetVaddr) + return Pop<VAddr>(); + else { + ASSERT_MSG(0, "remove the assert if multiprocess/IPC translation are implemented."); + // The buffer has already been copied to the static buffer by the kernel during + // translation + Pop<VAddr>(); // Pop the calling process buffer address + // and get the vaddr from the static buffers + return cmdbuf[(0x100 >> 2) + bufferInfo.buffer_id * 2 + 1]; + } +} + +inline VAddr RequestParser::PopMappedBuffer(size_t* data_size, + MappedBufferPermissions* buffer_perms) { + const u32 sbuffer_descriptor = Pop<u32>(); + MappedBufferDescInfo bufferInfo{sbuffer_descriptor}; + if (data_size != nullptr) + *data_size = bufferInfo.size; + if (buffer_perms != nullptr) + *buffer_perms = bufferInfo.perms; + return Pop<VAddr>(); +} + +} // namespace IPC diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h index c088b9a19..761fc4781 100644 --- a/src/core/hle/kernel/server_session.h +++ b/src/core/hle/kernel/server_session.h @@ -4,6 +4,7 @@ #pragma once +#include <memory> #include <string> #include "common/assert.h" #include "common/common_types.h" @@ -44,7 +45,8 @@ public: /** * Creates a pair of ServerSession and an associated ClientSession. - * @param name Optional name of the ports + * @param name Optional name of the ports. + * @param hle_handler Optional HLE handler for this server session. * @return The created session tuple */ static SessionPair CreateSessionPair( diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index c557a2279..6ab31c70b 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -11,7 +11,6 @@ #include <boost/container/flat_set.hpp> #include "common/common_types.h" #include "core/arm/arm_interface.h" -#include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/result.h" diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 60537f355..a00c75679 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -52,9 +52,14 @@ void Timer::Set(s64 initial, s64 interval) { initial_delay = initial; interval_delay = interval; - u64 initial_microseconds = initial / 1000; - CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, - callback_handle); + if (initial == 0) { + // Immediately invoke the callback + Signal(0); + } else { + u64 initial_microseconds = initial / 1000; + CoreTiming::ScheduleEvent(usToCycles(initial_microseconds), timer_callback_event_type, + callback_handle); + } } void Timer::Cancel() { @@ -72,6 +77,22 @@ void Timer::WakeupAllWaitingThreads() { signaled = false; } +void Timer::Signal(int cycles_late) { + LOG_TRACE(Kernel, "Timer %u fired", GetObjectId()); + + signaled = true; + + // Resume all waiting threads + WakeupAllWaitingThreads(); + + if (interval_delay != 0) { + // Reschedule the timer with the interval delay + u64 interval_microseconds = interval_delay / 1000; + CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, + timer_callback_event_type, callback_handle); + } +} + /// The timer callback event, called when a timer is fired static void TimerCallback(u64 timer_handle, int cycles_late) { SharedPtr<Timer> timer = @@ -82,19 +103,7 @@ static void TimerCallback(u64 timer_handle, int cycles_late) { return; } - LOG_TRACE(Kernel, "Timer %08" PRIx64 " fired", timer_handle); - - timer->signaled = true; - - // Resume all waiting threads - timer->WakeupAllWaitingThreads(); - - if (timer->interval_delay != 0) { - // Reschedule the timer with the interval delay - u64 interval_microseconds = timer->interval_delay / 1000; - CoreTiming::ScheduleEvent(usToCycles(interval_microseconds) - cycles_late, - timer_callback_event_type, timer_handle); - } + timer->Signal(cycles_late); } void TimersInit() { diff --git a/src/core/hle/kernel/timer.h b/src/core/hle/kernel/timer.h index c174f5664..b0f818933 100644 --- a/src/core/hle/kernel/timer.h +++ b/src/core/hle/kernel/timer.h @@ -54,6 +54,14 @@ public: void Cancel(); void Clear(); + /** + * Signals the timer, waking up any waiting threads and rescheduling it + * for the next interval. + * This method should not be called from outside the timer callback handler, + * lest multiple callback events get scheduled. + */ + void Signal(int cycles_late); + private: Timer(); ~Timer() override; diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 53864a3a7..cfefbbc64 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -20,6 +20,7 @@ enum class ErrorDescription : u32 { OS_InvalidBufferDescriptor = 48, MaxConnectionsReached = 52, WrongAddress = 53, + FS_RomFSNotFound = 100, FS_ArchiveNotMounted = 101, FS_FileNotFound = 112, FS_PathNotFound = 113, @@ -35,10 +36,13 @@ enum class ErrorDescription : u32 { OutofRangeOrMisalignedAddress = 513, // TODO(purpasmart): Check if this name fits its actual usage GPU_FirstInitialization = 519, + FS_ExeFSSectionNotFound = 567, + FS_CommandNotAllowed = 630, FS_InvalidReadFlag = 700, FS_InvalidPath = 702, FS_WriteBeyondEnd = 705, FS_UnsupportedOpenFlags = 760, + FS_IncorrectExeFSReadSize = 761, FS_UnexpectedFileOrDirectory = 770, InvalidSection = 1000, TooLarge = 1001, diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 615fe31ea..366d1eacf 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -18,6 +18,8 @@ #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" +#include "core/hw/aes/ccm.h" +#include "core/hw/aes/key.h" namespace Service { namespace APT { @@ -47,13 +49,13 @@ void SendParameter(const MessageParameter& parameter) { } void Initialize(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - u32 flags = cmd_buff[2]; - - cmd_buff[2] = IPC::CopyHandleDesc(2); - cmd_buff[3] = Kernel::g_handle_table.Create(notification_event).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(parameter_event).MoveFrom(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 + u32 app_id = rp.Pop<u32>(); + u32 flags = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).MoveFrom(), + Kernel::g_handle_table.Create(parameter_event).MoveFrom()); // TODO(bunnei): Check if these events are cleared every time Initialize is called. notification_event->Clear(); @@ -62,18 +64,16 @@ void Initialize(Service::Interface* self) { ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); lock->Release(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); } void GetSharedFont(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); if (!shared_font_mem) { LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); - cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); - cmd_buff[1] = -1; // TODO: Find the right error code + rb.Push<u32>(-1); // TODO: Find the right error code + rb.Skip(1 + 2, true); return; } @@ -85,103 +85,110 @@ void GetSharedFont(Service::Interface* self) { BCFNT::RelocateSharedFont(shared_font_mem, target_address); shared_font_relocated = true; } - cmd_buff[0] = IPC::MakeHeader(0x44, 2, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + rb.Push(RESULT_SUCCESS); // No error // Since the SharedMemory interface doesn't provide the address at which the memory was // allocated, the real APT service calculates this address by scanning the entire address space // (using svcQueryMemory) and searches for an allocation of the same size as the Shared Font. - cmd_buff[2] = target_address; - cmd_buff[3] = IPC::CopyHandleDesc(); - cmd_buff[4] = Kernel::g_handle_table.Create(shared_font_mem).MoveFrom(); + rb.Push(target_address); + rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom()); } void NotifyToWait(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x43, 1, 0); // 0x430040 + u32 app_id = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) app_id=%u", app_id); } void GetLockHandle(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1, 1, 0); // 0x10040 + // Bits [0:2] are the applet type (System, Library, etc) // Bit 5 tells the application that there's a pending APT parameter, // this will cause the app to wait until parameter_event is signaled. - u32 applet_attributes = cmd_buff[1]; - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - - cmd_buff[2] = applet_attributes; // Applet Attributes, this value is passed to Enable. - cmd_buff[3] = 0; // Least significant bit = power button state - cmd_buff[4] = IPC::CopyHandleDesc(); - cmd_buff[5] = Kernel::g_handle_table.Create(lock).MoveFrom(); - - LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", cmd_buff[5], + u32 applet_attributes = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. + rb.Push<u32>(0); // Least significant bit = power button state + Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).MoveFrom(); + rb.PushCopyHandles(handle_copy); + + LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy, applet_attributes); } void Enable(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 attributes = cmd_buff[1]; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - parameter_event->Signal(); // Let the application know that it has been started + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 + u32 attributes = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error + parameter_event->Signal(); // Let the application know that it has been started LOG_WARNING(Service_APT, "(STUBBED) called attributes=0x%08X", attributes); } 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>(AppletId::HomeMenu); // Home menu AppID - cmd_buff[5] = static_cast<u32>(AppletId::Application); // TODO(purpasmart96): Do this correctly + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 1, 0); // 0x50040 + u32 unk = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(5, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push<u32>(0); + rb.Push<u32>(0); + rb.Push(static_cast<u32>(AppletId::HomeMenu)); // Home menu AppID + rb.Push(static_cast<u32>(AppletId::Application)); // TODO(purpasmart96): Do this correctly LOG_WARNING(Service_APT, "(STUBBED) called unk=0x%08X", unk); } void IsRegistered(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 + u32 app_id = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error // TODO(Subv): An application is considered "registered" if it has already called APT::Enable // handle this properly once we implement multiprocess support. - cmd_buff[2] = 0; // Set to not registered by default + bool is_registered = false; // Set to not registered by default if (app_id == static_cast<u32>(AppletId::AnyLibraryApplet)) { - cmd_buff[2] = HLE::Applets::IsLibraryAppletRunning() ? 1 : 0; + is_registered = HLE::Applets::IsLibraryAppletRunning(); } else if (auto applet = HLE::Applets::Applet::Get(static_cast<AppletId>(app_id))) { - cmd_buff[2] = 1; // Set to registered + is_registered = true; // Set to registered } + rb.Push(is_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[1]; - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = static_cast<u32>(SignalType::None); // Signal type + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 1, 0); // 0xB0040 + u32 app_id = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(static_cast<u32>(SignalType::None)); // Signal type LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); } 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 buffer = cmd_buff[8]; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 4, 4); // 0xC0104 + u32 src_app_id = rp.Pop<u32>(); + u32 dst_app_id = rp.Pop<u32>(); + u32 signal_type = rp.Pop<u32>(); + u32 buffer_size = rp.Pop<u32>(); + Kernel::Handle handle = rp.PopHandle(); + size_t size; + VAddr buffer = rp.PopStaticBuffer(&size); std::shared_ptr<HLE::Applets::Applet> dest_applet = HLE::Applets::Applet::Get(static_cast<AppletId>(dst_app_id)); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (dest_applet == nullptr) { LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); - cmd_buff[1] = -1; // TODO(Subv): Find the right error code + rb.Push<u32>(-1); // TODO(Subv): Find the right error code return; } @@ -193,88 +200,104 @@ void SendParameter(Service::Interface* self) { param.buffer.resize(buffer_size); Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size()); - cmd_buff[1] = dest_applet->ReceiveParameter(param).raw; + rb.Push(dest_applet->ReceiveParameter(param)); - 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, buffer); + LOG_WARNING(Service_APT, + "(STUBBED) called src_app_id=0x%08X, dst_app_id=0x%08X, signal_type=0x%08X," + "buffer_size=0x%08X, handle=0x%08X, size=0x%08zX, in_param_buffer_ptr=0x%08X", + src_app_id, dst_app_id, signal_type, buffer_size, handle, size, buffer); } void ReceiveParameter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - u32 buffer_size = cmd_buff[2]; - VAddr buffer = cmd_buff[0x104 >> 2]; - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = next_parameter.sender_id; - cmd_buff[3] = next_parameter.signal; // Signal type - cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size - cmd_buff[5] = 0x10; - cmd_buff[6] = 0; - if (next_parameter.object != nullptr) - cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); - cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2; - cmd_buff[8] = buffer; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xD, 2, 0); // 0xD0080 + u32 app_id = rp.Pop<u32>(); + u32 buffer_size = rp.Pop<u32>(); + + size_t static_buff_size; + VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size); + if (buffer_size > static_buff_size) + LOG_WARNING( + Service_APT, + "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", + buffer_size, static_buff_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(next_parameter.sender_id); + rb.Push(next_parameter.signal); // Signal type + ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !"); + rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size + + rb.PushMoveHandles((next_parameter.object != nullptr) + ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() + : 0); + rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); - LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); } void GlanceParameter(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id = cmd_buff[1]; - u32 buffer_size = cmd_buff[2]; - VAddr buffer = cmd_buff[0x104 >> 2]; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xE, 2, 0); // 0xE0080 + u32 app_id = rp.Pop<u32>(); + u32 buffer_size = rp.Pop<u32>(); + + size_t static_buff_size; + VAddr buffer = rp.PeekStaticBuffer(0, &static_buff_size); + if (buffer_size > static_buff_size) + LOG_WARNING( + Service_APT, + "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", + buffer_size, static_buff_size); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[2] = next_parameter.sender_id; - cmd_buff[3] = next_parameter.signal; // Signal type - cmd_buff[4] = next_parameter.buffer.size(); // Parameter buffer size - cmd_buff[5] = 0x10; - cmd_buff[6] = 0; - if (next_parameter.object != nullptr) - cmd_buff[6] = Kernel::g_handle_table.Create(next_parameter.object).MoveFrom(); - cmd_buff[7] = (next_parameter.buffer.size() << 14) | 2; - cmd_buff[8] = buffer; + IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(next_parameter.sender_id); + rb.Push(next_parameter.signal); // Signal type + ASSERT_MSG(next_parameter.buffer.size() <= buffer_size, "Input static buffer is too small !"); + rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size - Memory::WriteBlock(buffer, next_parameter.buffer.data(), - std::min(static_cast<size_t>(buffer_size), next_parameter.buffer.size())); + rb.PushCopyHandles((next_parameter.object != nullptr) + ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() + : 0); + rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); + + Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); - LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08X", app_id, buffer_size); + LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); } 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); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100 + + u32 check_sender = rp.Pop<u32>(); + u32 sender_appid = rp.Pop<u32>(); + u32 check_receiver = rp.Pop<u32>(); + u32 receiver_appid = rp.Pop<u32>(); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(true); // Set to Success + + LOG_WARNING(Service_APT, "(STUBBED) called check_sender=0x%08X, sender_appid=0x%08X, " + "check_receiver=0x%08X, receiver_appid=0x%08X", + check_sender, sender_appid, check_receiver, receiver_appid); } void PrepareToStartApplication(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 title_info1 = cmd_buff[1]; - u32 title_info2 = cmd_buff[2]; - u32 title_info3 = cmd_buff[3]; - u32 title_info4 = cmd_buff[4]; - u32 flags = cmd_buff[5]; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x15, 5, 0); // 0x00150140 + u32 title_info1 = rp.Pop<u32>(); + u32 title_info2 = rp.Pop<u32>(); + u32 title_info3 = rp.Pop<u32>(); + u32 title_info4 = rp.Pop<u32>(); + u32 flags = rp.Pop<u32>(); if (flags & 0x00000100) { unknown_ns_state_field = 1; } - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) called title_info1=0x%08X, title_info2=0x%08X, title_info3=0x%08X," @@ -283,172 +306,188 @@ void PrepareToStartApplication(Service::Interface* self) { } void StartApplication(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 buffer1_size = cmd_buff[1]; - u32 buffer2_size = cmd_buff[2]; - u32 flag = cmd_buff[3]; - u32 size1 = cmd_buff[4]; - u32 buffer1_ptr = cmd_buff[5]; - u32 size2 = cmd_buff[6]; - u32 buffer2_ptr = cmd_buff[7]; - - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 3, 4); // 0x001B00C4 + u32 buffer1_size = rp.Pop<u32>(); + u32 buffer2_size = rp.Pop<u32>(); + u32 flag = rp.Pop<u32>(); + size_t size1; + VAddr buffer1_ptr = rp.PopStaticBuffer(&size1); + size_t size2; + VAddr buffer2_ptr = rp.PopStaticBuffer(&size2); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) called buffer1_size=0x%08X, buffer2_size=0x%08X, flag=0x%08X," - "size1=0x%08X, buffer1_ptr=0x%08X, size2=0x%08X, buffer2_ptr=0x%08X", + "size1=0x%08zX, buffer1_ptr=0x%08X, size2=0x%08zX, buffer2_ptr=0x%08X", buffer1_size, buffer2_size, flag, size1, buffer1_ptr, size2, buffer2_ptr); } void AppletUtility(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4B, 3, 2); // 0x004B00C2 // These are from 3dbrew - I'm not really sure what they're used for. - u32 command = cmd_buff[1]; - u32 buffer1_size = cmd_buff[2]; - u32 buffer2_size = cmd_buff[3]; - u32 buffer1_addr = cmd_buff[5]; - u32 buffer2_addr = cmd_buff[65]; + u32 utility_command = rp.Pop<u32>(); + u32 input_size = rp.Pop<u32>(); + u32 output_size = rp.Pop<u32>(); + VAddr input_addr = rp.PopStaticBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + VAddr output_addr = rp.PeekStaticBuffer(0); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, - "(STUBBED) called command=0x%08X, buffer1_size=0x%08X, buffer2_size=0x%08X, " - "buffer1_addr=0x%08X, buffer2_addr=0x%08X", - command, buffer1_size, buffer2_size, buffer1_addr, buffer2_addr); + "(STUBBED) called command=0x%08X, input_size=0x%08X, output_size=0x%08X, " + "input_addr=0x%08X, output_addr=0x%08X", + utility_command, input_size, output_size, input_addr, output_addr); } void SetAppCpuTimeLimit(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 value = cmd_buff[1]; - cpu_percent = cmd_buff[2]; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x4F, 2, 0); // 0x4F0080 + u32 value = rp.Pop<u32>(); + cpu_percent = rp.Pop<u32>(); 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 + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) called cpu_percent=%u, value=%u", cpu_percent, value); } void GetAppCpuTimeLimit(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 value = cmd_buff[1]; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x50, 1, 0); // 0x500040 + u32 value = rp.Pop<u32>(); 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 - cmd_buff[2] = cpu_percent; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(cpu_percent); LOG_WARNING(Service_APT, "(STUBBED) called value=%u", value); } void PrepareToStartLibraryApplet(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040 + AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { - cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; + rb.Push(HLE::Applets::Applet::Create(applet_id)); } LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); } void PreloadLibraryApplet(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 + AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } else { - cmd_buff[1] = HLE::Applets::Applet::Create(applet_id).raw; + rb.Push(HLE::Applets::Applet::Create(applet_id)); } LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); } void StartLibraryApplet(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - AppletId applet_id = static_cast<AppletId>(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084 + AppletId applet_id = static_cast<AppletId>(rp.Pop<u32>()); std::shared_ptr<HLE::Applets::Applet> applet = HLE::Applets::Applet::Get(applet_id); LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); if (applet == nullptr) { LOG_ERROR(Service_APT, "unknown applet id=%08X", applet_id); - cmd_buff[1] = -1; // TODO(Subv): Find the right error code + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, false); + rb.Push<u32>(-1); // TODO(Subv): Find the right error code return; } - size_t buffer_size = cmd_buff[2]; - VAddr buffer_addr = cmd_buff[6]; + size_t buffer_size = rp.Pop<u32>(); + Kernel::Handle handle = rp.PopHandle(); + VAddr buffer_addr = rp.PopStaticBuffer(); AppletStartupParameter parameter; - parameter.object = Kernel::g_handle_table.GetGeneric(cmd_buff[4]); + parameter.object = Kernel::g_handle_table.GetGeneric(handle); parameter.buffer.resize(buffer_size); Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size()); - cmd_buff[1] = applet->Start(parameter).raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(applet->Start(parameter)); } void CancelLibraryApplet(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 exiting = cmd_buff[1] & 0xFF; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3B, 1, 0); // 0x003B0040 + bool exiting = rp.Pop<bool>(); - cmd_buff[1] = 1; // TODO: Find the return code meaning + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push<u32>(1); // TODO: Find the return code meaning - LOG_WARNING(Service_APT, "(STUBBED) called exiting=%u", exiting); + LOG_WARNING(Service_APT, "(STUBBED) called exiting=%d", exiting); } void SetScreenCapPostPermission(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x55, 1, 0); // 0x00550040 - screen_capture_post_permission = static_cast<ScreencapPostPermission>(cmd_buff[1] & 0xF); + screen_capture_post_permission = static_cast<ScreencapPostPermission>(rp.Pop<u32>() & 0xF); - cmd_buff[0] = IPC::MakeHeader(0x55, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); // No error LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", screen_capture_post_permission); } void GetScreenCapPostPermission(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x56, 0, 0); // 0x00560000 - cmd_buff[0] = IPC::MakeHeader(0x56, 2, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = static_cast<u32>(screen_capture_post_permission); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error + rb.Push(static_cast<u32>(screen_capture_post_permission)); LOG_WARNING(Service_APT, "(STUBBED) screen_capture_post_permission=%u", screen_capture_post_permission); } void GetAppletInfo(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - auto app_id = static_cast<AppletId>(cmd_buff[1]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 1, 0); // 0x60040 + auto app_id = static_cast<AppletId>(rp.Pop<u32>()); if (auto applet = HLE::Applets::Applet::Get(app_id)) { // TODO(Subv): Get the title id for the current applet and write it in the response[2-3] - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[4] = static_cast<u32>(Service::FS::MediaType::NAND); - cmd_buff[5] = 1; // Registered - cmd_buff[6] = 1; // Loaded - cmd_buff[7] = 0; // Applet Attributes + IPC::RequestBuilder rb = rp.MakeBuilder(7, 0); + rb.Push(RESULT_SUCCESS); + u64 title_id = 0; + rb.Push(title_id); + rb.Push(static_cast<u32>(Service::FS::MediaType::NAND)); + rb.Push(true); // Registered + rb.Push(true); // Loaded + rb.Push<u32>(0); // Applet Attributes } else { - cmd_buff[1] = ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, - ErrorSummary::NotFound, ErrorLevel::Status) - .raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status)); } LOG_WARNING(Service_APT, "(stubbed) called appid=%u", app_id); } void GetStartupArgument(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 parameter_size = cmd_buff[1]; - StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(cmd_buff[2]); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x51, 2, 0); // 0x00510080 + u32 parameter_size = rp.Pop<u32>(); + StartupArgumentType startup_argument_type = static_cast<StartupArgumentType>(rp.Pop<u8>()); if (parameter_size >= 0x300) { LOG_ERROR( @@ -458,7 +497,14 @@ void GetStartupArgument(Service::Interface* self) { return; } - u32 addr = cmd_buff[65]; + size_t static_buff_size; + VAddr addr = rp.PeekStaticBuffer(0, &static_buff_size); + if (parameter_size > static_buff_size) + LOG_WARNING( + Service_APT, + "parameter_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", + parameter_size, static_buff_size); + if (addr && parameter_size) { Memory::ZeroBlock(addr, parameter_size); } @@ -466,30 +512,133 @@ void GetStartupArgument(Service::Interface* self) { LOG_WARNING(Service_APT, "(stubbed) called startup_argument_type=%u , parameter_size=0x%08x", startup_argument_type, parameter_size); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); + rb.PushStaticBuffer(addr, parameter_size, 0); +} + +void Wrap(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x46, 4, 4); + const u32 output_size = rp.Pop<u32>(); + const u32 input_size = rp.Pop<u32>(); + const u32 nonce_offset = rp.Pop<u32>(); + u32 nonce_size = rp.Pop<u32>(); + size_t desc_size; + IPC::MappedBufferPermissions desc_permission; + const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R); + const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W); + + // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't + // check the buffer size and writes data with potential overflow. + ASSERT_MSG(output_size == input_size + HW::AES::CCM_MAC_SIZE, + "input_size (%d) doesn't match to output_size (%d)", input_size, output_size); + + LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u", + output_size, input_size, nonce_offset, nonce_size); + + // Note: This weird nonce size modification is verified against real 3DS + nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE); + + // Reads nonce and concatenates the rest of the input as plaintext + HW::AES::CCMNonce nonce{}; + Memory::ReadBlock(input + nonce_offset, nonce.data(), nonce_size); + u32 pdata_size = input_size - nonce_size; + std::vector<u8> pdata(pdata_size); + Memory::ReadBlock(input, pdata.data(), nonce_offset); + Memory::ReadBlock(input + nonce_offset + nonce_size, pdata.data() + nonce_offset, + pdata_size - nonce_offset); + + // Encrypts the plaintext using AES-CCM + auto cipher = HW::AES::EncryptSignCCM(pdata, nonce, HW::AES::KeySlotID::APTWrap); + + // Puts the nonce to the beginning of the output, with ciphertext followed + Memory::WriteBlock(output, nonce.data(), nonce_size); + Memory::WriteBlock(output + nonce_size, cipher.data(), cipher.size()); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + rb.Push(RESULT_SUCCESS); + + // Unmap buffer + rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R); + rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W); +} + +void Unwrap(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x47, 4, 4); + const u32 output_size = rp.Pop<u32>(); + const u32 input_size = rp.Pop<u32>(); + const u32 nonce_offset = rp.Pop<u32>(); + u32 nonce_size = rp.Pop<u32>(); + size_t desc_size; + IPC::MappedBufferPermissions desc_permission; + const VAddr input = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == input_size && desc_permission == IPC::MappedBufferPermissions::R); + const VAddr output = rp.PopMappedBuffer(&desc_size, &desc_permission); + ASSERT(desc_size == output_size && desc_permission == IPC::MappedBufferPermissions::W); + + // Note: real 3DS still returns SUCCESS when the sizes don't match. It seems that it doesn't + // check the buffer size and writes data with potential overflow. + ASSERT_MSG(output_size == input_size - HW::AES::CCM_MAC_SIZE, + "input_size (%d) doesn't match to output_size (%d)", input_size, output_size); + + LOG_DEBUG(Service_APT, "called, output_size=%u, input_size=%u, nonce_offset=%u, nonce_size=%u", + output_size, input_size, nonce_offset, nonce_size); + + // Note: This weird nonce size modification is verified against real 3DS + nonce_size = std::min<u32>(nonce_size & ~3, HW::AES::CCM_NONCE_SIZE); + + // Reads nonce and cipher text + HW::AES::CCMNonce nonce{}; + Memory::ReadBlock(input, nonce.data(), nonce_size); + u32 cipher_size = input_size - nonce_size; + std::vector<u8> cipher(cipher_size); + Memory::ReadBlock(input + nonce_size, cipher.data(), cipher_size); + + // Decrypts the ciphertext using AES-CCM + auto pdata = HW::AES::DecryptVerifyCCM(cipher, nonce, HW::AES::KeySlotID::APTWrap); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 4); + if (!pdata.empty()) { + // Splits the plaintext and put the nonce in between + Memory::WriteBlock(output, pdata.data(), nonce_offset); + Memory::WriteBlock(output + nonce_offset, nonce.data(), nonce_size); + Memory::WriteBlock(output + nonce_offset + nonce_size, pdata.data() + nonce_offset, + pdata.size() - nonce_offset); + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_APT, "Failed to decrypt data"); + rb.Push(ResultCode(static_cast<ErrorDescription>(1), ErrorModule::PS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + } + + // Unmap buffer + rb.PushMappedBuffer(input, input_size, IPC::MappedBufferPermissions::R); + rb.PushMappedBuffer(output, output_size, IPC::MappedBufferPermissions::W); } void CheckNew3DSApp(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x101, 0, 0); // 0x01010000 + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (unknown_ns_state_field) { - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); } else { - PTM::CheckNew3DS(self); + PTM::CheckNew3DS(rb); } - cmd_buff[0] = IPC::MakeHeader(0x101, 2, 0); LOG_WARNING(Service_APT, "(STUBBED) called"); } void CheckNew3DS(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x102, 0, 0); // 0x01020000 + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); - PTM::CheckNew3DS(self); + PTM::CheckNew3DS(rb); - cmd_buff[0] = IPC::MakeHeader(0x102, 2, 0); LOG_WARNING(Service_APT, "(STUBBED) called"); } diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 80325361f..e63b61450 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -137,6 +137,46 @@ void Initialize(Service::Interface* self); void GetSharedFont(Service::Interface* self); /** + * APT::Wrap service function + * Inputs: + * 1 : Output buffer size + * 2 : Input buffer size + * 3 : Nonce offset to the input buffer + * 4 : Nonce size + * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA) + * 6 : Input buffer address + * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC) + * 8 : Output buffer address + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA) + * 3 : Input buffer address + * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC) + * 5 : Output buffer address + */ +void Wrap(Service::Interface* self); + +/** + * APT::Unwrap service function + * Inputs: + * 1 : Output buffer size + * 2 : Input buffer size + * 3 : Nonce offset to the output buffer + * 4 : Nonce size + * 5 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xA) + * 6 : Input buffer address + * 7 : Buffer mapping descriptor ((input_buffer_size << 4) | 0xC) + * 8 : Output buffer address + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xA) + * 3 : Input buffer address + * 4 : Buffer unmapping descriptor ((input_buffer_size << 4) | 0xC) + * 5 : Output buffer address + */ +void Unwrap(Service::Interface* self); + +/** * APT::NotifyToWait service function * Inputs: * 1 : AppID diff --git a/src/core/hle/service/apt/apt_a.cpp b/src/core/hle/service/apt/apt_a.cpp index 62dc2d61d..c496cba8d 100644 --- a/src/core/hle/service/apt/apt_a.cpp +++ b/src/core/hle/service/apt/apt_a.cpp @@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, + {0x00460104, Wrap, "Wrap"}, + {0x00470104, Unwrap, "Unwrap"}, {0x00480100, nullptr, "GetProgramInfo"}, {0x00490180, nullptr, "Reboot"}, {0x004A0040, nullptr, "GetCaptureInfo"}, diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index effd23dce..ec5668d05 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, + {0x00460104, Wrap, "Wrap"}, + {0x00470104, Unwrap, "Unwrap"}, {0x00480100, nullptr, "GetProgramInfo"}, {0x00490180, nullptr, "Reboot"}, {0x004A0040, nullptr, "GetCaptureInfo"}, diff --git a/src/core/hle/service/apt/apt_u.cpp b/src/core/hle/service/apt/apt_u.cpp index e06084a1e..9dd002590 100644 --- a/src/core/hle/service/apt/apt_u.cpp +++ b/src/core/hle/service/apt/apt_u.cpp @@ -78,8 +78,8 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00430040, NotifyToWait, "NotifyToWait"}, {0x00440000, GetSharedFont, "GetSharedFont"}, {0x00450040, nullptr, "GetWirelessRebootInfo"}, - {0x00460104, nullptr, "Wrap"}, - {0x00470104, nullptr, "Unwrap"}, + {0x00460104, Wrap, "Wrap"}, + {0x00470104, Unwrap, "Unwrap"}, {0x00480100, nullptr, "GetProgramInfo"}, {0x00490180, nullptr, "Reboot"}, {0x004A0040, nullptr, "GetCaptureInfo"}, diff --git a/src/core/hle/service/cam/cam.h b/src/core/hle/service/cam/cam.h index f6bff8bc6..34a9c8479 100644 --- a/src/core/hle/service/cam/cam.h +++ b/src/core/hle/service/cam/cam.h @@ -518,7 +518,7 @@ void FlipImage(Service::Interface* self); void SetDetailSize(Service::Interface* self); /** - * Sets camera resolution from preset resolution parameters. . + * Sets camera resolution from preset resolution parameters. * Inputs: * 0: 0x001F00C0 * 1: u8 selected camera diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 6f13cde27..4ddb1bc90 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -3,6 +3,8 @@ // Refer to the license.txt file included. #include <algorithm> +#include <array> +#include <cryptopp/sha.h> #include "common/file_util.h" #include "common/logging/log.h" #include "common/string_util.h" @@ -176,14 +178,29 @@ void SecureInfoGetRegion(Service::Interface* self) { } void GenHashConsoleUnique(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 app_id_salt = cmd_buff[1]; - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0x33646D6F ^ (app_id_salt & 0xFFFFF); // 3dmoo hash - cmd_buff[3] = 0x6F534841 ^ (app_id_salt & 0xFFFFF); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0); + const u32 app_id_salt = rp.Pop<u32>() & 0x000FFFFF; + + IPC::RequestBuilder rb = rp.MakeBuilder(3, 0); + + std::array<u8, 12> buffer; + const ResultCode result = GetConfigInfoBlock(ConsoleUniqueID2BlockID, 8, 2, buffer.data()); + rb.Push(result); + if (result.IsSuccess()) { + std::memcpy(&buffer[8], &app_id_salt, sizeof(u32)); + std::array<u8, CryptoPP::SHA256::DIGESTSIZE> hash; + CryptoPP::SHA256().CalculateDigest(hash.data(), buffer.data(), sizeof(buffer)); + u32 low, high; + memcpy(&low, &hash[hash.size() - 8], sizeof(u32)); + memcpy(&high, &hash[hash.size() - 4], sizeof(u32)); + rb.Push(low); + rb.Push(high); + } else { + rb.Push<u32>(0); + rb.Push<u32>(0); + } - LOG_WARNING(Service_CFG, "(STUBBED) called app_id_salt=0x%X", app_id_salt); + LOG_DEBUG(Service_CFG, "called app_id_salt=0x%X", app_id_salt); } void GetRegionCanadaUSA(Service::Interface* self) { diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 519c1f3a9..2ea956e0b 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -26,7 +26,7 @@ namespace FS { /// Supported archive types enum class ArchiveIdCode : u32 { - RomFS = 0x00000003, + SelfNCCH = 0x00000003, SaveData = 0x00000004, ExtSaveData = 0x00000006, SharedExtSaveData = 0x00000007, diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 337da1387..33b290699 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -54,15 +54,17 @@ static void Initialize(Service::Interface* self) { * 3 : File handle */ static void OpenFile(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + // The helper should be passed by argument to the function + IPC::RequestParser rp(Kernel::GetCommandBuffer(), {0x080201C2}); + rp.Pop<u32>(); // Always 0 ? - ArchiveHandle archive_handle = MakeArchiveHandle(cmd_buff[2], cmd_buff[3]); - auto filename_type = static_cast<FileSys::LowPathType>(cmd_buff[4]); - u32 filename_size = cmd_buff[5]; + ArchiveHandle archive_handle = rp.Pop<u64>(); + auto filename_type = static_cast<FileSys::LowPathType>(rp.Pop<u32>()); + u32 filename_size = rp.Pop<u32>(); FileSys::Mode mode; - mode.hex = cmd_buff[6]; - u32 attributes = cmd_buff[7]; // TODO(Link Mauve): do something with those attributes. - u32 filename_ptr = cmd_buff[9]; + mode.hex = rp.Pop<u32>(); + u32 attributes = rp.Pop<u32>(); // TODO(Link Mauve): do something with those attributes. + VAddr filename_ptr = rp.PopStaticBuffer(); FileSys::Path file_path(filename_type, filename_size, filename_ptr); LOG_DEBUG(Service_FS, "path=%s, mode=%u attrs=%u", file_path.DebugStr().c_str(), mode.hex, @@ -70,16 +72,17 @@ static void OpenFile(Service::Interface* self) { ResultVal<std::shared_ptr<File>> file_res = OpenFileFromArchive(archive_handle, file_path, mode); - cmd_buff[1] = file_res.Code().raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(file_res.Code()); if (file_res.Succeeded()) { std::shared_ptr<File> file = *file_res; auto sessions = ServerSession::CreateSessionPair(file->GetName(), file); file->ClientConnected(std::get<Kernel::SharedPtr<Kernel::ServerSession>>(sessions)); - cmd_buff[3] = Kernel::g_handle_table - .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) - .MoveFrom(); + rb.PushMoveHandles(Kernel::g_handle_table + .Create(std::get<Kernel::SharedPtr<Kernel::ClientSession>>(sessions)) + .MoveFrom()); } else { - cmd_buff[3] = 0; + rb.PushMoveHandles(0); LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); } } diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index a8c1331ed..a960778a7 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -4,6 +4,7 @@ #include "common/bit_field.h" #include "common/microprofile.h" +#include "core/core.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" @@ -118,10 +119,10 @@ static ResultCode WriteHWRegs(u32 base_address, u32 size_in_bytes, VAddr data_va * Updates sequential GSP GPU hardware registers using parallel arrays of source data and masks. * For each register, the value is updated only where the mask is high * - * @param base_address The address of the first register in the sequence + * @param base_address The address of the first register in the sequence * @param size_in_bytes The number of registers to update (size of data) - * @param data A pointer to the source data to use for updates - * @param masks A pointer to the masks + * @param data_vaddr A virtual address to the source data to use for updates + * @param masks_vaddr A virtual address to the masks * @return RESULT_SUCCESS if the parameters are valid, error code otherwise */ static ResultCode WriteHWRegsWithMask(u32 base_address, u32 size_in_bytes, VAddr data_vaddr, @@ -280,6 +281,7 @@ ResultCode SetBufferSwap(u32 screen_id, const FrameBufferInfo& info) { if (screen_id == 0) { MicroProfileFlip(); + Core::System::GetInstance().perf_stats.EndGameFrame(); } return RESULT_SUCCESS; @@ -705,6 +707,33 @@ static void ReleaseRight(Interface* self) { LOG_WARNING(Service_GSP, "called"); } +/** + * GSP_GPU::StoreDataCache service function + * + * This Function is a no-op, We aren't emulating the CPU cache any time soon. + * + * Inputs: + * 0 : Header code [0x001F0082] + * 1 : Address + * 2 : Size + * 3 : Value 0, some descriptor for the KProcess Handle + * 4 : KProcess handle + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void StoreDataCache(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + u32 address = cmd_buff[1]; + u32 size = cmd_buff[2]; + u32 process = cmd_buff[4]; + + cmd_buff[0] = IPC::MakeHeader(0x1F, 0x1, 0); + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + + LOG_DEBUG(Service_GSP, "(STUBBED) called address=0x%08X, size=0x%08X, process=0x%08X", address, + size, process); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010082, WriteHWRegs, "WriteHWRegs"}, {0x00020084, WriteHWRegsWithMask, "WriteHWRegsWithMask"}, @@ -736,7 +765,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001C0040, nullptr, "SetLedForceOff"}, {0x001D0040, nullptr, "SetTestCommand"}, {0x001E0080, nullptr, "SetInternalPriorities"}, - {0x001F0082, nullptr, "StoreDataCache"}, + {0x001F0082, StoreDataCache, "StoreDataCache"}, }; GSP_GPU::GSP_GPU() { diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index f14ab3811..b19e831fe 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -2,10 +2,14 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> +#include <atomic> #include <cmath> +#include <memory> #include "common/logging/log.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" +#include "core/frontend/input.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/hid/hid.h" @@ -32,8 +36,8 @@ static u32 next_touch_index; static u32 next_accelerometer_index; static u32 next_gyroscope_index; -static int enable_accelerometer_count = 0; // positive means enabled -static int enable_gyroscope_count = 0; // positive means enabled +static int enable_accelerometer_count; // positive means enabled +static int enable_gyroscope_count; // positive means enabled static int pad_update_event; static int accelerometer_update_event; @@ -44,6 +48,11 @@ constexpr u64 pad_update_ticks = BASE_CLOCK_RATE_ARM11 / 234; constexpr u64 accelerometer_update_ticks = BASE_CLOCK_RATE_ARM11 / 104; constexpr u64 gyroscope_update_ticks = BASE_CLOCK_RATE_ARM11 / 101; +static std::atomic<bool> is_device_reload_pending; +static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> + buttons; +static std::unique_ptr<Input::AnalogDevice> circle_pad; + static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions constexpr float TAN30 = 0.577350269f; @@ -74,14 +83,48 @@ static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { return state; } +static void LoadInputDevices() { + std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN, + Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END, + buttons.begin(), Input::CreateDevice<Input::ButtonDevice>); + circle_pad = Input::CreateDevice<Input::AnalogDevice>( + Settings::values.analogs[Settings::NativeAnalog::CirclePad]); +} + +static void UnloadInputDevices() { + for (auto& button : buttons) { + button.reset(); + } + circle_pad.reset(); +} + static void UpdatePadCallback(u64 userdata, int cycles_late) { SharedMem* mem = reinterpret_cast<SharedMem*>(shared_mem->GetPointer()); - PadState state = VideoCore::g_emu_window->GetPadState(); + if (is_device_reload_pending.exchange(false)) + LoadInputDevices(); + + PadState state; + using namespace Settings::NativeButton; + state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus()); + state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus()); + state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus()); + state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus()); + state.right.Assign(buttons[Right - BUTTON_HID_BEGIN]->GetStatus()); + state.left.Assign(buttons[Left - BUTTON_HID_BEGIN]->GetStatus()); + state.up.Assign(buttons[Up - BUTTON_HID_BEGIN]->GetStatus()); + state.down.Assign(buttons[Down - BUTTON_HID_BEGIN]->GetStatus()); + state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus()); + state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus()); + state.start.Assign(buttons[Start - BUTTON_HID_BEGIN]->GetStatus()); + state.select.Assign(buttons[Select - BUTTON_HID_BEGIN]->GetStatus()); // Get current circle pad position and update circle pad direction - s16 circle_pad_x, circle_pad_y; - std::tie(circle_pad_x, circle_pad_y) = VideoCore::g_emu_window->GetCirclePadState(); + float circle_pad_x_f, circle_pad_y_f; + std::tie(circle_pad_x_f, circle_pad_y_f) = circle_pad->GetStatus(); + constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position + s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS); + s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS); state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; mem->pad.current_state.hex = state.hex; @@ -313,6 +356,8 @@ void Init() { AddService(new HID_U_Interface); AddService(new HID_SPVR_Interface); + is_device_reload_pending.store(true); + using Kernel::MemoryPermission; shared_mem = SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, @@ -323,6 +368,9 @@ void Init() { next_accelerometer_index = 0; next_gyroscope_index = 0; + enable_accelerometer_count = 0; + enable_gyroscope_count = 0; + // Create event handles event_pad_or_touch_1 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch1"); event_pad_or_touch_2 = Event::Create(ResetType::OneShot, "HID:EventPadOrTouch2"); @@ -347,6 +395,11 @@ void Shutdown() { event_accelerometer = nullptr; event_gyroscope = nullptr; event_debug_pad = nullptr; + UnloadInputDevices(); +} + +void ReloadInputDevices() { + is_device_reload_pending.store(true); } } // namespace HID diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 21e66dfe0..b505cdcd5 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -39,15 +39,6 @@ struct PadState { BitField<10, 1, u32> x; BitField<11, 1, u32> y; - BitField<14, 1, u32> zl; - BitField<15, 1, u32> zr; - - BitField<20, 1, u32> touch; - - BitField<24, 1, u32> c_right; - BitField<25, 1, u32> c_left; - BitField<26, 1, u32> c_up; - BitField<27, 1, u32> c_down; BitField<28, 1, u32> circle_right; BitField<29, 1, u32> circle_left; BitField<30, 1, u32> circle_up; @@ -185,35 +176,6 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A); #undef ASSERT_REG_POSITION #endif // !defined(_MSC_VER) -// Pre-defined PadStates for single button presses -const PadState PAD_NONE = {{0}}; -const PadState PAD_A = {{1u << 0}}; -const PadState PAD_B = {{1u << 1}}; -const PadState PAD_SELECT = {{1u << 2}}; -const PadState PAD_START = {{1u << 3}}; -const PadState PAD_RIGHT = {{1u << 4}}; -const PadState PAD_LEFT = {{1u << 5}}; -const PadState PAD_UP = {{1u << 6}}; -const PadState PAD_DOWN = {{1u << 7}}; -const PadState PAD_R = {{1u << 8}}; -const PadState PAD_L = {{1u << 9}}; -const PadState PAD_X = {{1u << 10}}; -const PadState PAD_Y = {{1u << 11}}; - -const PadState PAD_ZL = {{1u << 14}}; -const PadState PAD_ZR = {{1u << 15}}; - -const PadState PAD_TOUCH = {{1u << 20}}; - -const PadState PAD_C_RIGHT = {{1u << 24}}; -const PadState PAD_C_LEFT = {{1u << 25}}; -const PadState PAD_C_UP = {{1u << 26}}; -const PadState PAD_C_DOWN = {{1u << 27}}; -const PadState PAD_CIRCLE_RIGHT = {{1u << 28}}; -const PadState PAD_CIRCLE_LEFT = {{1u << 29}}; -const PadState PAD_CIRCLE_UP = {{1u << 30}}; -const PadState PAD_CIRCLE_DOWN = {{1u << 31}}; - /** * HID::GetIPCHandles service function * Inputs: @@ -301,5 +263,8 @@ void Init(); /// Shutdown HID service void Shutdown(); + +/// Reload input devices. Used when input configuration changed +void ReloadInputDevices(); } } diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index 7f1731a50..7ac34a990 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -2,9 +2,6 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include "core/hle/kernel/event.h" -#include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" #include "core/hle/service/ir/ir_u.h" @@ -14,101 +11,18 @@ namespace Service { namespace IR { -static Kernel::SharedPtr<Kernel::Event> handle_event; -static Kernel::SharedPtr<Kernel::Event> conn_status_event; -static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; -static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory; - -void GetHandles(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0x4000000; - cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom(); -} - -void InitializeIrNopShared(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - u32 transfer_buff_size = cmd_buff[1]; - u32 recv_buff_size = cmd_buff[2]; - u32 unk1 = cmd_buff[3]; - u32 send_buff_size = cmd_buff[4]; - u32 unk2 = cmd_buff[5]; - u8 baud_rate = cmd_buff[6] & 0xFF; - Kernel::Handle handle = cmd_buff[8]; - - if (Kernel::g_handle_table.IsValid(handle)) { - transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); - transfer_shared_memory->name = "IR:TransferSharedMemory"; - } - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, " - "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X", - transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle); -} - -void RequireConnection(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - conn_status_event->Signal(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - -void Disconnect(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - -void GetConnectionStatusEvent(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom(); - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - -void FinalizeIrNop(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[1] = RESULT_SUCCESS.raw; - - LOG_WARNING(Service_IR, "(STUBBED) called"); -} - void Init() { - using namespace Kernel; - AddService(new IR_RST_Interface); AddService(new IR_U_Interface); AddService(new IR_User_Interface); - using Kernel::MemoryPermission; - shared_memory = SharedMemory::Create(nullptr, 0x1000, Kernel::MemoryPermission::ReadWrite, - Kernel::MemoryPermission::ReadWrite, 0, - Kernel::MemoryRegion::BASE, "IR:SharedMemory"); - transfer_shared_memory = nullptr; - - // Create event handle(s) - handle_event = Event::Create(ResetType::OneShot, "IR:HandleEvent"); - conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); + InitUser(); + InitRST(); } void Shutdown() { - transfer_shared_memory = nullptr; - shared_memory = nullptr; - handle_event = nullptr; - conn_status_event = nullptr; + ShutdownUser(); + ShutdownRST(); } } // namespace IR diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h index 72d44ce60..c741498e2 100644 --- a/src/core/hle/service/ir/ir.h +++ b/src/core/hle/service/ir/ir.h @@ -10,63 +10,6 @@ class Interface; namespace IR { -/** - * IR::GetHandles service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Translate header, used by the ARM11-kernel - * 3 : Shared memory handle - * 4 : Event handle - */ -void GetHandles(Interface* self); - -/** - * IR::InitializeIrNopShared service function - * Inputs: - * 1 : Size of transfer buffer - * 2 : Recv buffer size - * 3 : unknown - * 4 : Send buffer size - * 5 : unknown - * 6 : BaudRate (u8) - * 7 : 0 - * 8 : Handle of transfer shared memory - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void InitializeIrNopShared(Interface* self); - -/** - * IR::FinalizeIrNop service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void FinalizeIrNop(Interface* self); - -/** - * IR::GetConnectionStatusEvent service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - * 2 : Connection Status Event handle - */ -void GetConnectionStatusEvent(Interface* self); - -/** - * IR::Disconnect service function - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void Disconnect(Interface* self); - -/** - * IR::RequireConnection service function - * Inputs: - * 1 : unknown (u8), looks like always 1 - * Outputs: - * 1 : Result of function, 0 on success, otherwise error code - */ -void RequireConnection(Interface* self); - /// Initialize IR service void Init(); diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 1f10ebd3d..3f1275c53 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -2,12 +2,34 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" namespace Service { namespace IR { +static Kernel::SharedPtr<Kernel::Event> handle_event; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; + +/** + * IR::GetHandles service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Translate header, used by the ARM11-kernel + * 3 : Shared memory handle + * 4 : Event handle + */ +static void GetHandles(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[2] = 0x4000000; + cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom(); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010000, GetHandles, "GetHandles"}, {0x00020080, nullptr, "Initialize"}, @@ -19,5 +41,20 @@ IR_RST_Interface::IR_RST_Interface() { Register(FunctionTable); } +void InitRST() { + using namespace Kernel; + + shared_memory = + SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, + MemoryPermission::ReadWrite, 0, MemoryRegion::BASE, "IR:SharedMemory"); + + handle_event = Event::Create(ResetType::OneShot, "IR:HandleEvent"); +} + +void ShutdownRST() { + shared_memory = nullptr; + handle_event = nullptr; +} + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index a492e15c9..75b732627 100644 --- a/src/core/hle/service/ir/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -18,5 +18,8 @@ public: } }; +void InitRST(); +void ShutdownRST(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_u.cpp b/src/core/hle/service/ir/ir_u.cpp index 429615f31..ce00d5732 100644 --- a/src/core/hle/service/ir/ir_u.cpp +++ b/src/core/hle/service/ir/ir_u.cpp @@ -27,7 +27,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00100000, nullptr, "GetErrorStatus"}, {0x00110040, nullptr, "SetSleepModeActive"}, {0x00120040, nullptr, "SetSleepModeState"}, - // clang-format off + // clang-format on }; IR_U_Interface::IR_U_Interface() { diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index 6cff1d544..b326d7fc7 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -2,12 +2,112 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_user.h" namespace Service { namespace IR { +static Kernel::SharedPtr<Kernel::Event> conn_status_event; +static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory; + +/** + * IR::InitializeIrNopShared service function + * Inputs: + * 1 : Size of transfer buffer + * 2 : Recv buffer size + * 3 : unknown + * 4 : Send buffer size + * 5 : unknown + * 6 : BaudRate (u8) + * 7 : 0 + * 8 : Handle of transfer shared memory + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void InitializeIrNopShared(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + u32 transfer_buff_size = cmd_buff[1]; + u32 recv_buff_size = cmd_buff[2]; + u32 unk1 = cmd_buff[3]; + u32 send_buff_size = cmd_buff[4]; + u32 unk2 = cmd_buff[5]; + u8 baud_rate = cmd_buff[6] & 0xFF; + Kernel::Handle handle = cmd_buff[8]; + + if (Kernel::g_handle_table.IsValid(handle)) { + transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); + transfer_shared_memory->name = "IR:TransferSharedMemory"; + } + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, " + "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X", + transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle); +} + +/** + * IR::RequireConnection service function + * Inputs: + * 1 : unknown (u8), looks like always 1 + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void RequireConnection(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + conn_status_event->Signal(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + +/** + * IR::Disconnect service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void Disconnect(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + +/** + * IR::GetConnectionStatusEvent service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Connection Status Event handle + */ +static void GetConnectionStatusEvent(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom(); + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + +/** + * IR::FinalizeIrNop service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void FinalizeIrNop(Interface* self) { + u32* cmd_buff = Kernel::GetCommandBuffer(); + + cmd_buff[1] = RESULT_SUCCESS.raw; + + LOG_WARNING(Service_IR, "(STUBBED) called"); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010182, nullptr, "InitializeIrNop"}, {0x00020000, FinalizeIrNop, "FinalizeIrNop"}, @@ -41,5 +141,17 @@ IR_User_Interface::IR_User_Interface() { Register(FunctionTable); } +void InitUser() { + using namespace Kernel; + + transfer_shared_memory = nullptr; + conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); +} + +void ShutdownUser() { + transfer_shared_memory = nullptr; + conn_status_event = nullptr; +} + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h index 71c932ffa..3849bd923 100644 --- a/src/core/hle/service/ir/ir_user.h +++ b/src/core/hle/service/ir/ir_user.h @@ -18,5 +18,8 @@ public: } }; +void InitUser(); +void ShutdownUser(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 060d5a55f..3bc10dbdc 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -57,7 +57,7 @@ public: * @param is_crs true if the module itself is the static module * @returns ResultCode RESULT_SUCCESS on success, otherwise error code. */ - ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_addresss, + ResultCode Rebase(VAddr crs_address, u32 cro_size, VAddr data_segment_address, u32 data_segment_size, VAddr bss_segment_address, u32 bss_segment_size, bool is_crs); @@ -102,7 +102,7 @@ public: /** * Registers this module and adds it to the module list. * @param crs_address the virtual address of the static module - * @auto_link whether to register as an auto link module + * @param auto_link whether to register as an auto link module */ void Register(VAddr crs_address, bool auto_link); diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index 8d00a7577..7af76676b 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -6,6 +6,7 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/arm/arm_interface.h" +#include "core/core.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/service/ldr_ro/cro_helper.h" diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index 0be94322c..63c334cb2 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -19,7 +19,7 @@ void CheckSysUpdateAvailable(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = 0; // No update available - LOG_WARNING(Service_NWM, "(STUBBED) called"); + LOG_WARNING(Service_NIM, "(STUBBED) called"); } void Init() { diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 08fade320..ef6c5ebe3 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -1,16 +1,49 @@ -// Copyright 2014 Citra Emulator Project +// Copyright 2017 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cstring> +#include <unordered_map> +#include <vector> #include "common/common_types.h" #include "common/logging/log.h" +#include "core/core_timing.h" #include "core/hle/kernel/event.h" +#include "core/hle/kernel/shared_memory.h" +#include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" +#include "core/memory.h" namespace Service { namespace NWM { -static Kernel::SharedPtr<Kernel::Event> uds_handle_event; +// Event that is signaled every time the connection status changes. +static Kernel::SharedPtr<Kernel::Event> connection_status_event; + +// Shared memory provided by the application to store the receive buffer. +// This is not currently used. +static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory; + +// Connection status of this 3DS. +static ConnectionStatus connection_status{}; + +// Node information about the current 3DS. +// TODO(Subv): Keep an array of all nodes connected to the network, +// that data has to be retransmitted in every beacon frame. +static NodeInfo node_info; + +// Mapping of bind node ids to their respective events. +static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; + +// The WiFi network channel that the network is currently on. +// Since we're not actually interacting with physical radio waves, this is just a dummy value. +static u8 network_channel = DefaultNetworkChannel; + +// Information about the network that we're currently connected to. +static NetworkInfo network_info; + +// Event that will generate and send the 802.11 beacon frames. +static int beacon_broadcast_event; /** * NWM_UDS::Shutdown service function @@ -32,14 +65,14 @@ static void Shutdown(Interface* self) { /** * NWM_UDS::RecvBeaconBroadcastData service function + * Returns the raw beacon data for nearby networks that match the supplied WlanCommId. * Inputs: * 1 : Output buffer max size - * 2 : Unknown - * 3 : Unknown - * 4 : MAC address? - * 6-14 : Unknown, usually zero / uninitialized? - * 15 : WLan Comm ID - * 16 : This is the ID also located at offset 0xE in the CTR-generation structure. + * 2-3 : Unknown + * 4-5 : Host MAC address. + * 6-14 : Unused + * 15 : WLan Comm Id + * 16 : Id * 17 : Value 0 * 18 : Input handle * 19 : (Size<<4) | 12 @@ -77,42 +110,274 @@ static void RecvBeaconBroadcastData(Interface* self) { /** * NWM_UDS::Initialize service function * Inputs: - * 1 : Unknown - * 2-11 : Input Structure - * 12 : Unknown u16 + * 1 : Shared memory size + * 2-11 : Input NodeInfo Structure + * 12 : 2-byte Version * 13 : Value 0 - * 14 : Handle + * 14 : Shared memory handle * Outputs: * 0 : Return header * 1 : Result of function, 0 on success, otherwise error code * 2 : Value 0 - * 3 : Output handle + * 3 : Output event handle */ static void InitializeWithVersion(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 unk1 = cmd_buff[1]; - u32 unk2 = cmd_buff[12]; - u32 value = cmd_buff[13]; - u32 handle = cmd_buff[14]; - - // Because NWM service is not implemented at all, we stub the Initialize function with an error - // code instead of success to prevent games from using the service and from causing more issues. - // The error code is from a real 3DS with wifi off, thus believed to be "network disabled". - /* - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; - cmd_buff[3] = Kernel::g_handle_table.Create(uds_handle_event) - .MoveFrom(); // TODO(purpasmart): Verify if this is a event handle - */ - cmd_buff[0] = IPC::MakeHeader(0x1B, 1, 2); - cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(2), ErrorModule::UDS, - ErrorSummary::StatusChanged, ErrorLevel::Status) - .raw; - cmd_buff[2] = 0; - cmd_buff[3] = 0; - - LOG_WARNING(Service_NWM, "(STUBBED) called unk1=0x%08X, unk2=0x%08X, value=%u, handle=0x%08X", - unk1, unk2, value, handle); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1B, 12, 2); + + u32 sharedmem_size = rp.Pop<u32>(); + + // Update the node information with the data the game gave us. + rp.PopRaw(node_info); + + u16 version; + rp.PopRaw(version); + Kernel::Handle sharedmem_handle = rp.PopHandle(); + + recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle); + + ASSERT_MSG(recv_buffer_memory->size == sharedmem_size, "Invalid shared memory size."); + + // Reset the connection status, it contains all zeros after initialization, + // except for the actual status value. + connection_status = {}; + connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).MoveFrom()); + + LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", + sharedmem_size, version, sharedmem_handle); +} + +/** + * NWM_UDS::GetConnectionStatus service function. + * Returns the connection status structure for the currently open network connection. + * This structure contains information about the connection, + * like the number of connected nodes, etc. + * Inputs: + * 0 : Command header. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2-13 : Channel of the current WiFi network connection. + */ +static void GetConnectionStatus(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xB, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(connection_status); + + LOG_DEBUG(Service_NWM, "called"); +} + +/** + * NWM_UDS::Bind service function. + * Binds a BindNodeId to a data channel and retrieves a data event. + * Inputs: + * 1 : BindNodeId + * 2 : Receive buffer size. + * 3 : u8 Data channel to bind to. + * 4 : Network node id. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy handle descriptor. + * 3 : Data available event handle. + */ +static void Bind(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x12, 4, 0); + + u32 bind_node_id = rp.Pop<u32>(); + u32 recv_buffer_size = rp.Pop<u32>(); + u8 data_channel; + rp.PopRaw(data_channel); + u16 network_node_id; + rp.PopRaw(network_node_id); + + // TODO(Subv): Store the data channel and verify it when receiving data frames. + + LOG_DEBUG(Service_NWM, "called"); + + if (data_channel == 0) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + + // Create a new event for this bind node. + // TODO(Subv): Signal this event when new data is received on this data channel. + auto event = Kernel::Event::Create(Kernel::ResetType::OneShot, + "NWM::BindNodeEvent" + std::to_string(bind_node_id)); + bind_node_events[bind_node_id] = event; + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(event).MoveFrom()); +} + +/** + * NWM_UDS::BeginHostingNetwork service function. + * Creates a network and starts broadcasting its presence. + * Inputs: + * 1 : Passphrase buffer size. + * 3 : VAddr of the NetworkInfo structure. + * 5 : VAddr of the passphrase. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void BeginHostingNetwork(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1D, 1, 4); + + const u32 passphrase_size = rp.Pop<u32>(); + + size_t desc_size; + const VAddr network_info_address = rp.PopStaticBuffer(&desc_size, false); + ASSERT(desc_size == sizeof(NetworkInfo)); + const VAddr passphrase_address = rp.PopStaticBuffer(&desc_size, false); + ASSERT(desc_size == passphrase_size); + + // TODO(Subv): Store the passphrase and verify it when attempting a connection. + + LOG_DEBUG(Service_NWM, "called"); + + Memory::ReadBlock(network_info_address, &network_info, sizeof(NetworkInfo)); + + // The real UDS module throws a fatal error if this assert fails. + ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); + + connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); + connection_status.max_nodes = network_info.max_nodes; + + // There's currently only one node in the network (the host). + connection_status.total_nodes = 1; + // The host is always the first node + connection_status.network_node_id = 1; + node_info.network_node_id = 1; + // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. + connection_status.node_bitmask |= 1; + + // If the game has a preferred channel, use that instead. + if (network_info.channel != 0) + network_channel = network_info.channel; + + connection_status_event->Signal(); + + // Start broadcasting the network, send a beacon frame every 102.4ms. + CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU), + beacon_broadcast_event, 0); + + LOG_WARNING(Service_NWM, + "An UDS network has been created, but broadcasting it is unimplemented."); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); +} + +/** + * NWM_UDS::DestroyNetwork service function. + * Closes the network that we're currently hosting. + * Inputs: + * 0 : Command header. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void DestroyNetwork(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 0, 0); + + // TODO(Subv): Find out what happens if this is called while + // no network is being hosted. + + // Unschedule the beacon broadcast event. + CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); + + connection_status.status = static_cast<u8>(NetworkStatus::NotConnected); + connection_status_event->Signal(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NWM, "called"); +} + +/** + * NWM_UDS::GetChannel service function. + * Returns the WiFi channel in which the network we're connected to is transmitting. + * Inputs: + * 0 : Command header. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Channel of the current WiFi network connection. + */ +static void GetChannel(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + + bool is_connected = connection_status.status != static_cast<u32>(NetworkStatus::NotConnected); + + u8 channel = is_connected ? network_channel : 0; + + rb.Push(RESULT_SUCCESS); + rb.PushRaw(channel); + + LOG_DEBUG(Service_NWM, "called"); +} + +/** + * NWM_UDS::SetApplicationData service function. + * Updates the application data that is being broadcast in the beacon frames + * for the network that we're hosting. + * Inputs: + * 1 : Data size. + * 3 : VAddr of the data. + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Channel of the current WiFi network connection. + */ +static void SetApplicationData(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 1, 2); + + u32 size = rp.Pop<u32>(); + + size_t desc_size; + const VAddr address = rp.PopStaticBuffer(&desc_size, false); + ASSERT(desc_size == size); + + LOG_DEBUG(Service_NWM, "called"); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + if (size > ApplicationDataSize) { + rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + + network_info.application_data_size = size; + Memory::ReadBlock(address, network_info.application_data.data(), size); + + rb.Push(RESULT_SUCCESS); +} + +// Sends a 802.11 beacon frame with information about the current network. +static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { + // Don't do anything if we're not actually hosting a network + if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) + return; + + // TODO(Subv): Actually generate the beacon and send it. + + // Start broadcasting the network, send a beacon frame every 102.4ms. + CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, + beacon_broadcast_event, 0); } const Interface::FunctionInfo FunctionTable[] = { @@ -123,23 +388,23 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00050040, nullptr, "EjectClient"}, {0x00060000, nullptr, "EjectSpectator"}, {0x00070080, nullptr, "UpdateNetworkAttribute"}, - {0x00080000, nullptr, "DestroyNetwork"}, + {0x00080000, DestroyNetwork, "DestroyNetwork"}, {0x00090442, nullptr, "ConnectNetwork (deprecated)"}, {0x000A0000, nullptr, "DisconnectNetwork"}, - {0x000B0000, nullptr, "GetConnectionStatus"}, + {0x000B0000, GetConnectionStatus, "GetConnectionStatus"}, {0x000D0040, nullptr, "GetNodeInformation"}, {0x000E0006, nullptr, "DecryptBeaconData (deprecated)"}, {0x000F0404, RecvBeaconBroadcastData, "RecvBeaconBroadcastData"}, - {0x00100042, nullptr, "SetApplicationData"}, + {0x00100042, SetApplicationData, "SetApplicationData"}, {0x00110040, nullptr, "GetApplicationData"}, - {0x00120100, nullptr, "Bind"}, + {0x00120100, Bind, "Bind"}, {0x00130040, nullptr, "Unbind"}, {0x001400C0, nullptr, "PullPacket"}, {0x00150080, nullptr, "SetMaxSendDelay"}, {0x00170182, nullptr, "SendTo"}, - {0x001A0000, nullptr, "GetChannel"}, + {0x001A0000, GetChannel, "GetChannel"}, {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, - {0x001D0044, nullptr, "BeginHostingNetwork"}, + {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, {0x001E0084, nullptr, "ConnectToNetwork"}, {0x001F0006, nullptr, "DecryptBeaconData"}, {0x00200040, nullptr, "Flush"}, @@ -148,13 +413,25 @@ const Interface::FunctionInfo FunctionTable[] = { }; NWM_UDS::NWM_UDS() { - uds_handle_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::uds_handle_event"); + connection_status_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "NWM::connection_status_event"); Register(FunctionTable); + + beacon_broadcast_event = + CoreTiming::RegisterEvent("UDS::BeaconBroadcastCallback", BeaconBroadcastCallback); } NWM_UDS::~NWM_UDS() { - uds_handle_event = nullptr; + network_info = {}; + bind_node_events.clear(); + connection_status_event = nullptr; + recv_buffer_memory = nullptr; + + connection_status = {}; + connection_status.status = static_cast<u32>(NetworkStatus::NotConnected); + + CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); } } // namespace NWM diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 55db748f6..65349f9fd 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -4,6 +4,10 @@ #pragma once +#include <array> +#include <cstddef> +#include "common/common_types.h" +#include "common/swap.h" #include "core/hle/service/service.h" // Local-WLAN service @@ -11,6 +15,68 @@ namespace Service { namespace NWM { +const size_t ApplicationDataSize = 0xC8; +const u8 DefaultNetworkChannel = 11; + +// Number of milliseconds in a TU. +const double MillisecondsPerTU = 1.024; +// Interval measured in TU, the default value is 100TU = 102.4ms +const u16 DefaultBeaconInterval = 100; + +struct NodeInfo { + u64_le friend_code_seed; + std::array<u16_le, 10> username; + INSERT_PADDING_BYTES(4); + u16_le network_node_id; + INSERT_PADDING_BYTES(6); +}; + +static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); + +enum class NetworkStatus { + NotConnected = 3, + ConnectedAsHost = 6, + ConnectedAsClient = 9, + ConnectedAsSpectator = 10, +}; + +struct ConnectionStatus { + u32_le status; + INSERT_PADDING_WORDS(1); + u16_le network_node_id; + INSERT_PADDING_BYTES(2); + INSERT_PADDING_BYTES(32); + u8 total_nodes; + u8 max_nodes; + u16_le node_bitmask; +}; + +static_assert(sizeof(ConnectionStatus) == 0x30, "ConnectionStatus has incorrect size."); + +struct NetworkInfo { + std::array<u8, 6> host_mac_address; + u8 channel; + INSERT_PADDING_BYTES(1); + u8 initialized; + INSERT_PADDING_BYTES(3); + std::array<u8, 3> oui_value; + u8 oui_type; + // This field is received as BigEndian from the game. + u32_be wlan_comm_id; + u8 id; + INSERT_PADDING_BYTES(1); + u16_be attributes; + u32_be network_id; + u8 total_nodes; + u8 max_nodes; + INSERT_PADDING_BYTES(2); + INSERT_PADDING_BYTES(0x1F); + u8 application_data_size; + std::array<u8, ApplicationDataSize> application_data; +}; + +static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); + class NWM_UDS final : public Interface { public: NWM_UDS(); diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 8ff808fd9..e373ed47a 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -92,8 +92,7 @@ void GetSoftwareClosedFlag(Service::Interface* self) { LOG_WARNING(Service_PTM, "(STUBBED) called"); } -void CheckNew3DS(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +void CheckNew3DS(IPC::RequestBuilder& rb) { const bool is_new_3ds = Settings::values.is_new_3ds; if (is_new_3ds) { @@ -101,12 +100,17 @@ void CheckNew3DS(Service::Interface* self) { "settings. Citra does not fully support New 3DS emulation yet!"); } - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = is_new_3ds ? 1 : 0; + rb.Push(RESULT_SUCCESS); + rb.Push(is_new_3ds); LOG_WARNING(Service_PTM, "(STUBBED) called isNew3DS = 0x%08x", static_cast<u32>(is_new_3ds)); } +void CheckNew3DS(Service::Interface* self) { + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x40A, 0, 0); // 0x040A0000 + CheckNew3DS(rb); +} + void Init() { AddService(new PTM_Gets); AddService(new PTM_Play); @@ -134,9 +138,9 @@ void Init() { ASSERT_MSG(archive_result.Succeeded(), "Could not open the PTM SharedExtSaveData archive!"); FileSys::Path gamecoin_path("/gamecoin.dat"); + Service::FS::CreateFileInArchive(*archive_result, gamecoin_path, sizeof(GameCoin)); FileSys::Mode open_mode = {}; open_mode.write_flag.Assign(1); - open_mode.create_flag.Assign(1); // Open the file and write the default gamecoin information auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index a1a628012..683fb445b 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -5,6 +5,7 @@ #pragma once #include "common/common_types.h" +#include "core/hle/ipc_helpers.h" namespace Service { @@ -97,6 +98,7 @@ void GetSoftwareClosedFlag(Interface* self); * 2: u8 output: 0 = Old3DS, 1 = New3DS. */ void CheckNew3DS(Interface* self); +void CheckNew3DS(IPC::RequestBuilder& rb); /// Initialize the PTM service void Init(); diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h index a7ba7688f..e6a5f1417 100644 --- a/src/core/hle/service/service.h +++ b/src/core/hle/service/service.h @@ -10,6 +10,7 @@ #include <boost/container/flat_map.hpp> #include "common/common_types.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/thread.h" #include "core/hle/result.h" diff --git a/src/core/hle/service/soc_u.cpp b/src/core/hle/service/soc_u.cpp index dcc5c3c90..530614e6f 100644 --- a/src/core/hle/service/soc_u.cpp +++ b/src/core/hle/service/soc_u.cpp @@ -362,18 +362,18 @@ static void Socket(Interface* self) { return; } - u32 socket_handle = static_cast<u32>(::socket(domain, type, protocol)); + u32 ret = static_cast<u32>(::socket(domain, type, protocol)); - if ((s32)socket_handle != SOCKET_ERROR_VALUE) - open_sockets[socket_handle] = {socket_handle, true}; + if ((s32)ret != SOCKET_ERROR_VALUE) + open_sockets[ret] = {ret, true}; int result = 0; - if ((s32)socket_handle == SOCKET_ERROR_VALUE) - result = TranslateError(GET_ERRNO); + if ((s32)ret == SOCKET_ERROR_VALUE) + ret = TranslateError(GET_ERRNO); cmd_buffer[0] = IPC::MakeHeader(2, 2, 0); cmd_buffer[1] = result; - cmd_buffer[2] = socket_handle; + cmd_buffer[2] = ret; } static void Bind(Interface* self) { @@ -393,15 +393,15 @@ static void Bind(Interface* self) { sockaddr sock_addr = CTRSockAddr::ToPlatform(ctr_sock_addr); - int res = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len)); + int ret = ::bind(socket_handle, &sock_addr, std::max<u32>(sizeof(sock_addr), len)); int result = 0; - if (res != 0) - result = TranslateError(GET_ERRNO); + if (ret != 0) + ret = TranslateError(GET_ERRNO); cmd_buffer[0] = IPC::MakeHeader(5, 2, 0); cmd_buffer[1] = result; - cmd_buffer[2] = res; + cmd_buffer[2] = ret; } static void Fcntl(Interface* self) { @@ -426,8 +426,7 @@ static void Fcntl(Interface* self) { #else int ret = ::fcntl(socket_handle, F_GETFL, 0); if (ret == SOCKET_ERROR_VALUE) { - result = TranslateError(GET_ERRNO); - posix_ret = -1; + posix_ret = TranslateError(GET_ERRNO); return; } posix_ret = 0; @@ -439,8 +438,7 @@ static void Fcntl(Interface* self) { unsigned long tmp = (ctr_arg & 4 /* O_NONBLOCK */) ? 1 : 0; int ret = ioctlsocket(socket_handle, FIONBIO, &tmp); if (ret == SOCKET_ERROR_VALUE) { - result = TranslateError(GET_ERRNO); - posix_ret = -1; + posix_ret = TranslateError(GET_ERRNO); return; } auto iter = open_sockets.find(socket_handle); @@ -449,8 +447,7 @@ static void Fcntl(Interface* self) { #else int flags = ::fcntl(socket_handle, F_GETFL, 0); if (flags == SOCKET_ERROR_VALUE) { - result = TranslateError(GET_ERRNO); - posix_ret = -1; + posix_ret = TranslateError(GET_ERRNO); return; } @@ -460,15 +457,13 @@ static void Fcntl(Interface* self) { int ret = ::fcntl(socket_handle, F_SETFL, flags); if (ret == SOCKET_ERROR_VALUE) { - result = TranslateError(GET_ERRNO); - posix_ret = -1; + posix_ret = TranslateError(GET_ERRNO); return; } #endif } else { LOG_ERROR(Service_SOC, "Unsupported command (%d) in fcntl call", ctr_cmd); - result = TranslateError(EINVAL); // TODO: Find the correct error - posix_ret = -1; + posix_ret = TranslateError(EINVAL); // TODO: Find the correct error return; } } @@ -481,7 +476,7 @@ static void Listen(Interface* self) { int ret = ::listen(socket_handle, backlog); int result = 0; if (ret != 0) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[0] = IPC::MakeHeader(3, 2, 0); cmd_buffer[1] = result; @@ -504,7 +499,7 @@ static void Accept(Interface* self) { int result = 0; if ((s32)ret == SOCKET_ERROR_VALUE) { - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); } else { CTRSockAddr ctr_addr = CTRSockAddr::FromPlatform(addr); Memory::WriteBlock(cmd_buffer[0x104 >> 2], &ctr_addr, sizeof(ctr_addr)); @@ -545,7 +540,7 @@ static void Close(Interface* self) { int result = 0; if (ret != 0) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[2] = ret; cmd_buffer[1] = result; @@ -589,7 +584,7 @@ static void SendTo(Interface* self) { int result = 0; if (ret == SOCKET_ERROR_VALUE) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[2] = ret; cmd_buffer[1] = result; @@ -638,7 +633,7 @@ static void RecvFrom(Interface* self) { int result = 0; int total_received = ret; if (ret == SOCKET_ERROR_VALUE) { - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); total_received = 0; } else { // Write only the data we received to avoid overwriting parts of the buffer with zeros @@ -673,7 +668,7 @@ static void Poll(Interface* self) { std::vector<pollfd> platform_pollfd(nfds); std::transform(ctr_fds.begin(), ctr_fds.end(), platform_pollfd.begin(), CTRPollFD::ToPlatform); - const int ret = ::poll(platform_pollfd.data(), nfds, timeout); + int ret = ::poll(platform_pollfd.data(), nfds, timeout); // Now update the output pollfd structure std::transform(platform_pollfd.begin(), platform_pollfd.end(), ctr_fds.begin(), @@ -683,7 +678,7 @@ static void Poll(Interface* self) { int result = 0; if (ret == SOCKET_ERROR_VALUE) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[1] = result; cmd_buffer[2] = ret; @@ -710,7 +705,7 @@ static void GetSockName(Interface* self) { int result = 0; if (ret != 0) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[2] = ret; cmd_buffer[1] = result; @@ -724,7 +719,7 @@ static void Shutdown(Interface* self) { int ret = ::shutdown(socket_handle, how); int result = 0; if (ret != 0) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[2] = ret; cmd_buffer[1] = result; } @@ -750,7 +745,7 @@ static void GetPeerName(Interface* self) { int result = 0; if (ret != 0) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[2] = ret; cmd_buffer[1] = result; @@ -777,7 +772,7 @@ static void Connect(Interface* self) { int ret = ::connect(socket_handle, &input_addr, sizeof(input_addr)); int result = 0; if (ret != 0) - result = TranslateError(GET_ERRNO); + ret = TranslateError(GET_ERRNO); cmd_buffer[0] = IPC::MakeHeader(6, 2, 0); cmd_buffer[1] = result; @@ -815,7 +810,7 @@ static void GetSockOpt(Interface* self) { int optname = TranslateSockOpt(cmd_buffer[3]); socklen_t optlen = (socklen_t)cmd_buffer[4]; - int ret = -1; + int ret = 0; int err = 0; if (optname < 0) { @@ -830,9 +825,8 @@ static void GetSockOpt(Interface* self) { // >> 2 = convert to u32 offset instead of byte offset (cmd_buffer = u32*) char* optval = reinterpret_cast<char*>(Memory::GetPointer(cmd_buffer[0x104 >> 2])); - ret = ::getsockopt(socket_handle, level, optname, optval, &optlen); - err = 0; - if (ret == SOCKET_ERROR_VALUE) { + err = ::getsockopt(socket_handle, level, optname, optval, &optlen); + if (err == SOCKET_ERROR_VALUE) { err = TranslateError(GET_ERRNO); } } @@ -849,7 +843,7 @@ static void SetSockOpt(Interface* self) { u32 level = cmd_buffer[2]; int optname = TranslateSockOpt(cmd_buffer[3]); - int ret = -1; + int ret = 0; int err = 0; if (optname < 0) { @@ -862,9 +856,8 @@ static void SetSockOpt(Interface* self) { socklen_t optlen = static_cast<socklen_t>(cmd_buffer[4]); const char* optval = reinterpret_cast<const char*>(Memory::GetPointer(cmd_buffer[8])); - ret = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen)); - err = 0; - if (ret == SOCKET_ERROR_VALUE) { + err = static_cast<u32>(::setsockopt(socket_handle, level, optname, optval, optlen)); + if (err == SOCKET_ERROR_VALUE) { err = TranslateError(GET_ERRNO); } } diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index 31bb466fc..c0837d49d 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -189,11 +189,9 @@ static void SetSpacialDithering(Interface* self) { * 2 : u8, 0 = Disabled, 1 = Enabled */ static void GetSpacialDithering(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - cmd_buff[0] = IPC::MakeHeader(0xA, 2, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = spacial_dithering_enabled; + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0xA, 2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(spacial_dithering_enabled != 0); LOG_WARNING(Service_Y2R, "(STUBBED) called"); } @@ -281,37 +279,39 @@ static void GetTransferEndEvent(Interface* self) { } static void SetSendingY(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + // The helper should be passed by argument to the function + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00100102); + conversion.src_Y.address = rp.Pop<u32>(); + conversion.src_Y.image_size = rp.Pop<u32>(); + conversion.src_Y.transfer_unit = rp.Pop<u32>(); + conversion.src_Y.gap = rp.Pop<u32>(); + Kernel::Handle src_process_handle = rp.PopHandle(); - conversion.src_Y.address = cmd_buff[1]; - conversion.src_Y.image_size = cmd_buff[2]; - conversion.src_Y.transfer_unit = cmd_buff[3]; - conversion.src_Y.gap = cmd_buff[4]; - - cmd_buff[0] = IPC::MakeHeader(0x10, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " "src_process_handle=0x%08X", conversion.src_Y.image_size, conversion.src_Y.transfer_unit, conversion.src_Y.gap, - cmd_buff[6]); + src_process_handle); } static void SetSendingU(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - - conversion.src_U.address = cmd_buff[1]; - conversion.src_U.image_size = cmd_buff[2]; - conversion.src_U.transfer_unit = cmd_buff[3]; - conversion.src_U.gap = cmd_buff[4]; + // The helper should be passed by argument to the function + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x00110102); + conversion.src_U.address = rp.Pop<u32>(); + conversion.src_U.image_size = rp.Pop<u32>(); + conversion.src_U.transfer_unit = rp.Pop<u32>(); + conversion.src_U.gap = rp.Pop<u32>(); + Kernel::Handle src_process_handle = rp.PopHandle(); - cmd_buff[0] = IPC::MakeHeader(0x11, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_Y2R, "called image_size=0x%08X, transfer_unit=%hu, transfer_stride=%hu, " "src_process_handle=0x%08X", conversion.src_U.image_size, conversion.src_U.transfer_unit, conversion.src_U.gap, - cmd_buff[6]); + src_process_handle); } static void SetSendingV(Interface* self) { @@ -561,11 +561,10 @@ static void GetAlpha(Interface* self) { } static void SetDitheringWeightParams(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - std::memcpy(&dithering_weight_params, &cmd_buff[1], sizeof(DitheringWeightParams)); - - cmd_buff[0] = IPC::MakeHeader(0x24, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x24, 8, 0); // 0x240200 + rp.PopRaw(dithering_weight_params); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); LOG_DEBUG(Service_Y2R, "called"); } diff --git a/src/core/hle/shared_page.cpp b/src/core/hle/shared_page.cpp index d0d92487d..5978ccdd4 100644 --- a/src/core/hle/shared_page.cpp +++ b/src/core/hle/shared_page.cpp @@ -6,6 +6,7 @@ #include <cstring> #include <ctime> #include "core/core_timing.h" +#include "core/hle/service/ptm/ptm.h" #include "core/hle/shared_page.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -73,6 +74,12 @@ void Init() { // Some games wait until this value becomes 0x1, before asking running_hw shared_page.unknown_value = 0x1; + // Set to a completely full battery + shared_page.battery_state.charge_level.Assign( + static_cast<u8>(Service::PTM::ChargeLevels::CompletelyFull)); + shared_page.battery_state.is_adapter_connected.Assign(1); + shared_page.battery_state.is_charging.Assign(1); + update_time_event = CoreTiming::RegisterEvent("SharedPage::UpdateTimeCallback", UpdateTimeCallback); CoreTiming::ScheduleEvent(0, update_time_event); diff --git a/src/core/hle/shared_page.h b/src/core/hle/shared_page.h index 106e47efc..864695ae1 100644 --- a/src/core/hle/shared_page.h +++ b/src/core/hle/shared_page.h @@ -10,6 +10,7 @@ * write access, according to 3dbrew; this is not emulated) */ +#include "common/bit_field.h" #include "common/common_funcs.h" #include "common/common_types.h" #include "common/swap.h" @@ -29,6 +30,13 @@ struct DateTime { }; static_assert(sizeof(DateTime) == 0x20, "Datetime size is wrong"); +union BatteryState { + u8 raw; + BitField<0, 1, u8> is_adapter_connected; + BitField<1, 1, u8> is_charging; + BitField<2, 3, u8> charge_level; +}; + struct SharedPageDef { // Most of these names are taken from the 3dbrew page linked above. u32_le date_time_counter; // 0 @@ -44,7 +52,7 @@ struct SharedPageDef { INSERT_PADDING_BYTES(0x80 - 0x68); // 68 float_le sliderstate_3d; // 80 u8 ledstate_3d; // 84 - INSERT_PADDING_BYTES(1); // 85 + BatteryState battery_state; // 85 u8 unknown_value; // 86 INSERT_PADDING_BYTES(0xA0 - 0x87); // 87 u64_le menu_title_id; // A0 diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 96db39ad9..2db823c61 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -467,8 +467,8 @@ static void Break(u8 break_reason) { } /// Used to output a message on a debug hardware unit - does nothing on a retail unit -static void OutputDebugString(const char* string) { - LOG_DEBUG(Debug_Emulated, "%s", string); +static void OutputDebugString(const char* string, int len) { + LOG_DEBUG(Debug_Emulated, "%.*s", len, string); } /// Get resource limit @@ -556,11 +556,21 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent break; } - if (processor_id == THREADPROCESSORID_1 || processor_id == THREADPROCESSORID_ALL || - (processor_id == THREADPROCESSORID_DEFAULT && - Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1)) { - LOG_WARNING(Kernel_SVC, - "Newly created thread is allowed to be run in the SysCore, unimplemented."); + if (processor_id == THREADPROCESSORID_ALL) { + LOG_INFO(Kernel_SVC, + "Newly created thread is allowed to be run in any Core, unimplemented."); + } + + if (processor_id == THREADPROCESSORID_DEFAULT && + Kernel::g_current_process->ideal_processor == THREADPROCESSORID_1) { + LOG_WARNING( + Kernel_SVC, + "Newly created thread is allowed to be run in the SysCore (Core1), unimplemented."); + } + + if (processor_id == THREADPROCESSORID_1) { + LOG_ERROR(Kernel_SVC, + "Newly created thread must run in the SysCore (Core1), unimplemented."); } CASCADE_RESULT(SharedPtr<Thread> thread, Kernel::Thread::Create(name, entry_point, priority, @@ -837,6 +847,11 @@ static ResultCode SetTimer(Kernel::Handle handle, s64 initial, s64 interval) { LOG_TRACE(Kernel_SVC, "called timer=0x%08X", handle); + if (initial < 0 || interval < 0) { + return ResultCode(ErrorDescription::OutOfRange, ErrorModule::Kernel, + ErrorSummary::InvalidArgument, ErrorLevel::Permanent); + } + SharedPtr<Timer> timer = Kernel::g_handle_table.Get<Timer>(handle); if (timer == nullptr) return ERR_INVALID_HANDLE; |
