diff options
Diffstat (limited to 'src/core')
39 files changed, 658 insertions, 99 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b16a89990..ea09819e5 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -144,6 +144,7 @@ set(SRCS hle/service/nwm/nwm_tst.cpp hle/service/nwm/nwm_uds.cpp hle/service/nwm/uds_beacon.cpp + hle/service/nwm/uds_data.cpp hle/service/pm_app.cpp hle/service/ptm/ptm.cpp hle/service/ptm/ptm_gets.cpp @@ -341,6 +342,7 @@ set(HEADERS hle/service/nwm/nwm_tst.h hle/service/nwm/nwm_uds.h hle/service/nwm/uds_beacon.h + hle/service/nwm/uds_data.h hle/service/pm_app.h hle/service/ptm/ptm.h hle/service/ptm/ptm_gets.h diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 2abdfe1b3..5e6002f4e 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -229,9 +229,18 @@ void Wrap() { u32 retval = func(¶m_1, ¶m_2, reinterpret_cast<const char*>(Memory::GetPointer(PARAM(2))), PARAM(3)) .raw; - // The first out parameter is moved into R2 and the second is moved into R1. - Core::CPU().SetReg(1, param_2); - Core::CPU().SetReg(2, param_1); + Core::CPU().SetReg(1, param_1); + Core::CPU().SetReg(2, param_2); + FuncReturn(retval); +} + +template <ResultCode func(Kernel::Handle*, Kernel::Handle*)> +void Wrap() { + Kernel::Handle param_1 = 0; + Kernel::Handle param_2 = 0; + u32 retval = func(¶m_1, ¶m_2).raw; + Core::CPU().SetReg(1, param_1); + Core::CPU().SetReg(2, param_2); FuncReturn(retval); } diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h index b3b60e7df..64aa61460 100644 --- a/src/core/hle/kernel/errors.h +++ b/src/core/hle/kernel/errors.h @@ -13,6 +13,7 @@ enum { OutOfHandles = 19, SessionClosedByRemote = 26, PortNameTooLong = 30, + NoPendingSessions = 35, WrongPermission = 46, InvalidBufferDescriptor = 48, MaxConnectionsReached = 52, @@ -94,5 +95,9 @@ constexpr ResultCode ERR_OUT_OF_RANGE_KERNEL(ErrorDescription::OutOfRange, Error ErrorLevel::Permanent); // 0xD8E007FD constexpr ResultCode RESULT_TIMEOUT(ErrorDescription::Timeout, ErrorModule::OS, ErrorSummary::StatusChanged, ErrorLevel::Info); +/// Returned when Accept() is called on a port with no sessions to be accepted. +constexpr ResultCode ERR_NO_PENDING_SESSIONS(ErrCodes::NoPendingSessions, ErrorModule::OS, + ErrorSummary::WouldBlock, + ErrorLevel::Permanent); // 0xD8401823 } // namespace Kernel diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 6cf1886cf..5ebe2eca4 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -23,6 +23,11 @@ void SessionRequestHandler::ClientDisconnected(SharedPtr<ServerSession> server_s boost::range::remove_erase(connected_sessions, server_session); } +HLERequestContext::HLERequestContext(SharedPtr<ServerSession> session) + : session(std::move(session)) { + cmd_buf[0] = 0; +} + HLERequestContext::~HLERequestContext() = default; SharedPtr<Object> HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const { @@ -62,10 +67,13 @@ ResultCode HLERequestContext::PopulateFromIncomingCommandBuffer(const u32_le* sr ASSERT(i + num_handles <= command_size); // TODO(yuriks): Return error for (u32 j = 0; j < num_handles; ++j) { Handle handle = src_cmdbuf[i]; - SharedPtr<Object> object = src_table.GetGeneric(handle); - ASSERT(object != nullptr); // TODO(yuriks): Return error - if (descriptor == IPC::DescriptorType::MoveHandle) { - src_table.Close(handle); + SharedPtr<Object> object = nullptr; + if (handle != 0) { + object = src_table.GetGeneric(handle); + ASSERT(object != nullptr); // TODO(yuriks): Return error + if (descriptor == IPC::DescriptorType::MoveHandle) { + src_table.Close(handle); + } } cmd_buf[i++] = AddOutgoingHandle(std::move(object)); @@ -107,9 +115,11 @@ ResultCode HLERequestContext::WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, P ASSERT(i + num_handles <= command_size); for (u32 j = 0; j < num_handles; ++j) { SharedPtr<Object> object = GetIncomingHandle(cmd_buf[i]); - - // TODO(yuriks): Figure out the proper error handling for if this fails - Handle handle = dst_table.Create(object).Unwrap(); + Handle handle = 0; + if (object != nullptr) { + // TODO(yuriks): Figure out the proper error handling for if this fails + handle = dst_table.Create(object).Unwrap(); + } dst_cmdbuf[i++] = handle; } break; diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h index cbb109d8f..35795fc1d 100644 --- a/src/core/hle/kernel/hle_ipc.h +++ b/src/core/hle/kernel/hle_ipc.h @@ -84,6 +84,7 @@ protected: */ class HLERequestContext { public: + HLERequestContext(SharedPtr<ServerSession> session); ~HLERequestContext(); /// Returns a pointer to the IPC command buffer for this request. @@ -118,14 +119,14 @@ public: */ void ClearIncomingObjects(); -private: - friend class Service::ServiceFrameworkBase; - + /// Populates this context with data from the requesting process/thread. ResultCode PopulateFromIncomingCommandBuffer(const u32_le* src_cmdbuf, Process& src_process, HandleTable& src_table); + /// Writes data from this context back to the requesting process/thread. ResultCode WriteToOutgoingCommandBuffer(u32_le* dst_cmdbuf, Process& dst_process, HandleTable& dst_table) const; +private: std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf; SharedPtr<ServerSession> session; // TODO(yuriks): Check common usage of this and optimize size accordingly diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 804f23b1c..496d07cb5 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -166,7 +166,7 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin auto vma = address_space .MapBackingMemory(mapping.address, target_pointer + offset_into_region, mapping.size, memory_state) - .MoveFrom(); + .Unwrap(); address_space.Reprotect(vma, mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); } @@ -176,14 +176,14 @@ void MapSharedPages(VMManager& address_space) { .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, reinterpret_cast<u8*>(&ConfigMem::config_mem), Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) - .MoveFrom(); + .Unwrap(); address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); auto shared_page_vma = address_space .MapBackingMemory(Memory::SHARED_PAGE_VADDR, reinterpret_cast<u8*>(&SharedPage::shared_page), Memory::SHARED_PAGE_SIZE, MemoryState::Shared) - .MoveFrom(); + .Unwrap(); address_space.Reprotect(shared_page_vma, VMAPermission::Read); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 1c31ec950..522ad2333 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -151,6 +151,8 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { } VAddr Process::GetLinearHeapAreaAddress() const { + // Starting from system version 8.0.0 a new linear heap layout is supported to allow usage of + // the extra RAM in the n3DS. return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; } diff --git a/src/core/hle/kernel/server_port.cpp b/src/core/hle/kernel/server_port.cpp index 4d20c39a1..49a9cdfa3 100644 --- a/src/core/hle/kernel/server_port.cpp +++ b/src/core/hle/kernel/server_port.cpp @@ -5,8 +5,10 @@ #include <tuple> #include "common/assert.h" #include "core/hle/kernel/client_port.h" +#include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/server_port.h" +#include "core/hle/kernel/server_session.h" #include "core/hle/kernel/thread.h" namespace Kernel { @@ -14,6 +16,16 @@ namespace Kernel { ServerPort::ServerPort() {} ServerPort::~ServerPort() {} +ResultVal<SharedPtr<ServerSession>> ServerPort::Accept() { + if (pending_sessions.empty()) { + return ERR_NO_PENDING_SESSIONS; + } + + auto session = std::move(pending_sessions.back()); + pending_sessions.pop_back(); + return MakeResult(std::move(session)); +} + bool ServerPort::ShouldWait(Thread* thread) const { // If there are no pending sessions, we wait until a new one is added. return pending_sessions.size() == 0; diff --git a/src/core/hle/kernel/server_port.h b/src/core/hle/kernel/server_port.h index f1419cd46..6fe7c7f2f 100644 --- a/src/core/hle/kernel/server_port.h +++ b/src/core/hle/kernel/server_port.h @@ -14,6 +14,7 @@ namespace Kernel { class ClientPort; +class ServerSession; class SessionRequestHandler; class ServerPort final : public WaitObject { @@ -41,6 +42,12 @@ public: } /** + * Accepts a pending incoming connection on this port. If there are no pending sessions, will + * return ERR_NO_PENDING_SESSIONS. + */ + ResultVal<SharedPtr<ServerSession>> Accept(); + + /** * Sets the HLE handler template for the port. ServerSessions crated by connecting to this port * will inherit a reference to this handler. */ @@ -50,8 +57,8 @@ public: std::string name; ///< Name of port (optional) - std::vector<SharedPtr<WaitObject>> - pending_sessions; ///< ServerSessions waiting to be accepted by the port + /// ServerSessions waiting to be accepted by the port + std::vector<SharedPtr<ServerSession>> pending_sessions; /// This session's HLE request handler template (optional) /// ServerSessions created from this port inherit a reference to this handler. diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp index 970eac5fe..337896abf 100644 --- a/src/core/hle/kernel/server_session.cpp +++ b/src/core/hle/kernel/server_session.cpp @@ -81,7 +81,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) { ServerSession::SessionPair ServerSession::CreateSessionPair(const std::string& name, SharedPtr<ClientPort> port) { - auto server_session = ServerSession::Create(name + "_Server").MoveFrom(); + auto server_session = ServerSession::Create(name + "_Server").Unwrap(); SharedPtr<ClientSession> client_session(new ClientSession); client_session->name = name + "_Client"; diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 75ce626f8..f5f2eb2f7 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -389,7 +389,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); - thread->callback_handle = wakeup_callback_handle_table.Create(thread).MoveFrom(); + thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); thread->owner_process = g_current_process; // Find the next available TLS index, and mark it as used @@ -484,7 +484,7 @@ SharedPtr<Thread> SetupMainThread(u32 entry_point, s32 priority) { auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, Memory::HEAP_VADDR_END); - SharedPtr<Thread> thread = thread_res.MoveFrom(); + SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 diff --git a/src/core/hle/kernel/timer.cpp b/src/core/hle/kernel/timer.cpp index 6f2cf3b02..d7ec93672 100644 --- a/src/core/hle/kernel/timer.cpp +++ b/src/core/hle/kernel/timer.cpp @@ -30,7 +30,7 @@ SharedPtr<Timer> Timer::Create(ResetType reset_type, std::string name) { timer->name = std::move(name); timer->initial_delay = 0; timer->interval_delay = 0; - timer->callback_handle = timer_callback_handle_table.Create(timer).MoveFrom(); + timer->callback_handle = timer_callback_handle_table.Create(timer).Unwrap(); return timer; } diff --git a/src/core/hle/result.h b/src/core/hle/result.h index 5f2cdbb96..47b6e2b23 100644 --- a/src/core/hle/result.h +++ b/src/core/hle/result.h @@ -388,13 +388,14 @@ public: } /// Asserts that the result succeeded and returns a reference to it. - T& Unwrap() { + T& Unwrap() & { ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); return **this; } - T&& MoveFrom() { - return std::move(Unwrap()); + T&& Unwrap() && { + ASSERT_MSG(Succeeded(), "Tried to Unwrap empty ResultVal"); + return std::move(**this); } private: diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 4c587e3c8..25e7b777d 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -55,8 +55,8 @@ void Initialize(Service::Interface* self) { 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()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), + Kernel::g_handle_table.Create(parameter_event).Unwrap()); // TODO(bunnei): Check if these events are cleared every time Initialize is called. notification_event->Clear(); @@ -93,7 +93,7 @@ void GetSharedFont(Service::Interface* self) { // 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. rb.Push(target_address); - rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(shared_font_mem).Unwrap()); } void NotifyToWait(Service::Interface* self) { @@ -115,7 +115,7 @@ void GetLockHandle(Service::Interface* self) { 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(); + Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); rb.PushCopyHandles(handle_copy); LOG_WARNING(Service_APT, "(STUBBED) called handle=0x%08X applet_attributes=0x%08X", handle_copy, @@ -231,7 +231,7 @@ void ReceiveParameter(Service::Interface* self) { 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() + ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() : 0); rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); @@ -261,7 +261,7 @@ void GlanceParameter(Service::Interface* self) { rb.Push(static_cast<u32>(next_parameter.buffer.size())); // Parameter buffer size rb.PushCopyHandles((next_parameter.object != nullptr) - ? Kernel::g_handle_table.Create(next_parameter.object).MoveFrom() + ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() : 0); rb.PushStaticBuffer(buffer, static_cast<u32>(next_parameter.buffer.size()), 0); diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index 7394c844f..c9f9e9d95 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -347,7 +347,7 @@ void GetVsyncInterruptEvent(Service::Interface* self) { int port = *port_select.begin(); rb.Push(RESULT_SUCCESS); rb.PushCopyHandles( - Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).MoveFrom()); + Kernel::g_handle_table.Create(ports[port].vsync_interrupt_event).Unwrap()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); rb.Push(ERROR_INVALID_ENUM_VALUE); @@ -366,7 +366,7 @@ void GetBufferErrorInterruptEvent(Service::Interface* self) { int port = *port_select.begin(); rb.Push(RESULT_SUCCESS); rb.PushCopyHandles( - Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).MoveFrom()); + Kernel::g_handle_table.Create(ports[port].buffer_error_interrupt_event).Unwrap()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); rb.Push(ERROR_INVALID_ENUM_VALUE); @@ -400,7 +400,7 @@ void SetReceiving(Service::Interface* self) { } rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(port.completion_event).Unwrap()); } else { LOG_ERROR(Service_CAM, "invalid port_select=%u", port_select.m_val); rb.Push(ERROR_INVALID_ENUM_VALUE); diff --git a/src/core/hle/service/cecd/cecd.cpp b/src/core/hle/service/cecd/cecd.cpp index bd9814244..421006a9e 100644 --- a/src/core/hle/service/cecd/cecd.cpp +++ b/src/core/hle/service/cecd/cecd.cpp @@ -31,8 +31,8 @@ void GetCecStateAbbreviated(Service::Interface* self) { void GetCecInfoEventHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).MoveFrom(); // Event handle + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(cecinfo_event).Unwrap(); // Event handle LOG_WARNING(Service_CECD, "(STUBBED) called"); } @@ -40,8 +40,8 @@ void GetCecInfoEventHandle(Service::Interface* self) { void GetChangeStateEventHandle(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).MoveFrom(); // Event handle + cmd_buff[1] = RESULT_SUCCESS.raw; // No error + cmd_buff[3] = Kernel::g_handle_table.Create(change_state_event).Unwrap(); // Event handle LOG_WARNING(Service_CECD, "(STUBBED) called"); } diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 5a7878b31..6624f1711 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -406,7 +406,7 @@ ResultCode UpdateConfigNANDSavegame() { auto config_result = Service::FS::OpenFileFromArchive(cfg_system_save_data_archive, path, mode); ASSERT_MSG(config_result.Succeeded(), "could not open file"); - auto config = config_result.MoveFrom(); + auto config = std::move(config_result).Unwrap(); config->backend->Write(0, CONFIG_SAVEFILE_SIZE, 1, cfg_config_file_buffer.data()); return RESULT_SUCCESS; @@ -560,7 +560,7 @@ ResultCode LoadConfigNANDSaveFile() { // Read the file if it already exists if (config_result.Succeeded()) { - auto config = config_result.MoveFrom(); + auto config = std::move(config_result).Unwrap(); config->backend->Read(0, CONFIG_SAVEFILE_SIZE, cfg_config_file_buffer.data()); return RESULT_SUCCESS; } diff --git a/src/core/hle/service/csnd_snd.cpp b/src/core/hle/service/csnd_snd.cpp index 1455f20ca..9471ec1ef 100644 --- a/src/core/hle/service/csnd_snd.cpp +++ b/src/core/hle/service/csnd_snd.cpp @@ -51,8 +51,8 @@ static void Initialize(Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = IPC::CopyHandleDesc(2); - cmd_buff[3] = Kernel::g_handle_table.Create(mutex).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(mutex).Unwrap(); + cmd_buff[4] = Kernel::g_handle_table.Create(shared_memory).Unwrap(); LOG_WARNING(Service_CSND, "(STUBBED) called"); } diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 363066d14..7d746054f 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -168,7 +168,7 @@ static void GetSemaphoreEventHandle(Service::Interface* self) { cmd_buff[0] = IPC::MakeHeader(0x16, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; // No error // cmd_buff[2] not set - cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).MoveFrom(); // Event handle + cmd_buff[3] = Kernel::g_handle_table.Create(semaphore_event).Unwrap(); // Event handle LOG_WARNING(Service_DSP, "(STUBBED) called"); } diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 3605ef175..033fbc9aa 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -311,7 +311,7 @@ ResultVal<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handl if (backend.Failed()) return backend.Code(); - auto file = std::shared_ptr<File>(new File(backend.MoveFrom(), path)); + auto file = std::shared_ptr<File>(new File(std::move(backend).Unwrap(), path)); return MakeResult<std::shared_ptr<File>>(std::move(file)); } @@ -401,7 +401,7 @@ ResultVal<std::shared_ptr<Directory>> OpenDirectoryFromArchive(ArchiveHandle arc if (backend.Failed()) return backend.Code(); - auto directory = std::shared_ptr<Directory>(new Directory(backend.MoveFrom(), path)); + auto directory = std::shared_ptr<Directory>(new Directory(std::move(backend).Unwrap(), path)); return MakeResult<std::shared_ptr<Directory>>(std::move(directory)); } diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index 34e1783ec..b9eab7838 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -87,7 +87,7 @@ static void OpenFile(Service::Interface* self) { file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); rb.PushMoveHandles( - Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom()); + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap()); } else { rb.PushMoveHandles(0); LOG_ERROR(Service_FS, "failed to get a handle for file %s", file_path.DebugStr().c_str()); @@ -153,7 +153,7 @@ static void OpenFileDirectly(Service::Interface* self) { file->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); cmd_buff[3] = - Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap(); } else { cmd_buff[3] = 0; LOG_ERROR(Service_FS, "failed to get a handle for file %s mode=%u attributes=%u", @@ -420,7 +420,7 @@ static void OpenDirectory(Service::Interface* self) { directory->ClientConnected(std::get<SharedPtr<ServerSession>>(sessions)); cmd_buff[3] = - Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).MoveFrom(); + Kernel::g_handle_table.Create(std::get<SharedPtr<ClientSession>>(sessions)).Unwrap(); } else { LOG_ERROR(Service_FS, "failed to get a handle for directory type=%d size=%d data=%s", dirname_type, dirname_size, dir_path.DebugStr().c_str()); diff --git a/src/core/hle/service/gsp_gpu.cpp b/src/core/hle/service/gsp_gpu.cpp index 6ff0f4812..bc964ec60 100644 --- a/src/core/hle/service/gsp_gpu.cpp +++ b/src/core/hle/service/gsp_gpu.cpp @@ -389,8 +389,8 @@ static void RegisterInterruptRelayQueue(Interface* self) { } else { cmd_buff[1] = RESULT_SUCCESS.raw; } - cmd_buff[2] = g_thread_id++; // Thread ID - cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).MoveFrom(); // GSP shared memory + cmd_buff[2] = g_thread_id++; // Thread ID + cmd_buff[4] = Kernel::g_handle_table.Create(g_shared_memory).Unwrap(); // GSP shared memory g_interrupt_event->Signal(); // TODO(bunnei): Is this correct? diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 5255f6dc8..2014b8461 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -253,12 +253,12 @@ void GetIPCHandles(Service::Interface* self) { cmd_buff[1] = 0; // No error cmd_buff[2] = 0x14000000; // IPC Command Structure translate-header // TODO(yuriks): Return error from SendSyncRequest is this fails (part of IPC marshalling) - cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).MoveFrom(); - cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).MoveFrom(); - cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).MoveFrom(); - cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).MoveFrom(); - cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(Service::HID::shared_mem).Unwrap(); + cmd_buff[4] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_1).Unwrap(); + cmd_buff[5] = Kernel::g_handle_table.Create(Service::HID::event_pad_or_touch_2).Unwrap(); + cmd_buff[6] = Kernel::g_handle_table.Create(Service::HID::event_accelerometer).Unwrap(); + cmd_buff[7] = Kernel::g_handle_table.Create(Service::HID::event_gyroscope).Unwrap(); + cmd_buff[8] = Kernel::g_handle_table.Create(Service::HID::event_debug_pad).Unwrap(); } void EnableAccelerometer(Service::Interface* self) { diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 0de698003..837413f93 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -145,8 +145,8 @@ static void GetHandles(Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); rb.Push(RESULT_SUCCESS); - rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(), - Kernel::g_handle_table.Create(Service::IR::update_event).MoveFrom()); + rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).Unwrap(), + Kernel::g_handle_table.Create(Service::IR::update_event).Unwrap()); } /** diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index fdecdce64..fbdf7a465 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -337,7 +337,7 @@ void GetReceiveEvent(Interface* self) { IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2); rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).Unwrap()); LOG_INFO(Service_IR, "called"); } @@ -354,7 +354,7 @@ void GetSendEvent(Interface* self) { IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2); rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).Unwrap()); LOG_INFO(Service_IR, "called"); } @@ -394,7 +394,7 @@ static void GetConnectionStatusEvent(Interface* self) { IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2); rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).Unwrap()); LOG_INFO(Service_IR, "called"); } diff --git a/src/core/hle/service/mic_u.cpp b/src/core/hle/service/mic_u.cpp index 35212b59b..23e1ff094 100644 --- a/src/core/hle/service/mic_u.cpp +++ b/src/core/hle/service/mic_u.cpp @@ -160,7 +160,7 @@ static void IsSampling(Interface* self) { static void GetBufferFullEvent(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; // No error - cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(buffer_full_event).Unwrap(); LOG_WARNING(Service_MIC, "(STUBBED) called"); } diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp index b44a9f668..cb09ed0b7 100644 --- a/src/core/hle/service/nfc/nfc.cpp +++ b/src/core/hle/service/nfc/nfc.cpp @@ -95,7 +95,7 @@ void GetTagInRangeEvent(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xB, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(tag_in_range_event).Unwrap(); LOG_WARNING(Service_NFC, "(STUBBED) called"); } @@ -105,7 +105,7 @@ void GetTagOutOfRangeEvent(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xC, 1, 2); cmd_buff[1] = RESULT_SUCCESS.raw; cmd_buff[2] = IPC::CopyHandleDesc(); - cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(tag_out_of_range_event).Unwrap(); LOG_WARNING(Service_NFC, "(STUBBED) called"); } diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index e92900d48..6dbdff044 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -15,6 +15,7 @@ #include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" #include "core/hle/service/nwm/uds_beacon.h" +#include "core/hle/service/nwm/uds_data.h" #include "core/memory.h" namespace Service { @@ -190,7 +191,7 @@ static void InitializeWithVersion(Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", sharedmem_size, version, sharedmem_handle); @@ -265,7 +266,7 @@ static void Bind(Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(event).MoveFrom()); + rb.PushCopyHandles(Kernel::g_handle_table.Create(event).Unwrap()); } /** @@ -373,6 +374,80 @@ static void DestroyNetwork(Interface* self) { } /** + * NWM_UDS::SendTo service function. + * Sends a data frame to the UDS network we're connected to. + * Inputs: + * 0 : Command header. + * 1 : Unknown. + * 2 : u16 Destination network node id. + * 3 : u8 Data channel. + * 4 : Buffer size >> 2 + * 5 : Data size + * 6 : Flags + * 7 : Input buffer descriptor + * 8 : Input buffer address + * Outputs: + * 0 : Return header + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SendTo(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x17, 6, 2); + + rp.Skip(1, false); + u16 dest_node_id = rp.Pop<u16>(); + u8 data_channel = rp.Pop<u8>(); + rp.Skip(1, false); + u32 data_size = rp.Pop<u32>(); + u32 flags = rp.Pop<u32>(); + + size_t desc_size; + const VAddr input_address = rp.PopStaticBuffer(&desc_size, false); + ASSERT(desc_size == data_size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) { + rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + if (dest_node_id == connection_status.network_node_id) { + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Status)); + return; + } + + // TODO(Subv): Do something with the flags. + + constexpr size_t MaxSize = 0x5C6; + if (data_size > MaxSize) { + rb.Push(ResultCode(ErrorDescription::TooLarge, ErrorModule::UDS, + ErrorSummary::WrongArgument, ErrorLevel::Usage)); + return; + } + + std::vector<u8> data(data_size); + Memory::ReadBlock(input_address, data.data(), data.size()); + + // TODO(Subv): Increment the sequence number after each sent packet. + u16 sequence_number = 0; + std::vector<u8> data_payload = GenerateDataPayload( + data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); + + // TODO(Subv): Retrieve the MAC address of the dest_node_id and our own to encrypt + // and encapsulate the payload. + + // TODO(Subv): Send the frame. + + rb.Push(RESULT_SUCCESS); + + LOG_WARNING(Service_NWM, "(STUB) called dest_node_id=%u size=%u flags=%u channel=%u", + static_cast<u32>(dest_node_id), data_size, flags, static_cast<u32>(data_channel)); +} + +/** * NWM_UDS::GetChannel service function. * Returns the WiFi channel in which the network we're connected to is transmitting. * Inputs: @@ -543,6 +618,42 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { beacon_broadcast_event, 0); } +/* + * Returns an available index in the nodes array for the + * currently-hosted UDS network. + */ +static u32 GetNextAvailableNodeId() { + ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), + "Can not accept clients if we're not hosting a network"); + + for (unsigned index = 0; index < connection_status.max_nodes; ++index) { + if ((connection_status.node_bitmask & (1 << index)) == 0) + return index; + } + + // Any connection attempts to an already full network should have been refused. + ASSERT_MSG(false, "No available connection slots in the network"); +} + +/* + * Called when a client connects to an UDS network we're hosting, + * updates the connection status and signals the update event. + * @param network_node_id Network Node Id of the connecting client. + */ +void OnClientConnected(u16 network_node_id) { + ASSERT_MSG(connection_status.status == static_cast<u32>(NetworkStatus::ConnectedAsHost), + "Can not accept clients if we're not hosting a network"); + ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, + "Can not accept connections on a full network"); + + u32 node_id = GetNextAvailableNodeId(); + connection_status.node_bitmask |= 1 << node_id; + connection_status.changed_nodes |= 1 << node_id; + connection_status.nodes[node_id] = network_node_id; + connection_status.total_nodes++; + connection_status_event->Signal(); +} + const Interface::FunctionInfo FunctionTable[] = { {0x00010442, nullptr, "Initialize (deprecated)"}, {0x00020000, nullptr, "Scrap"}, @@ -564,7 +675,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00130040, nullptr, "Unbind"}, {0x001400C0, nullptr, "PullPacket"}, {0x00150080, nullptr, "SetMaxSendDelay"}, - {0x00170182, nullptr, "SendTo"}, + {0x00170182, SendTo, "SendTo"}, {0x001A0000, GetChannel, "GetChannel"}, {0x001B0302, InitializeWithVersion, "InitializeWithVersion"}, {0x001D0044, BeginHostingNetwork, "BeginHostingNetwork"}, diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp new file mode 100644 index 000000000..8c6742dba --- /dev/null +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -0,0 +1,278 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> +#include <cryptopp/aes.h> +#include <cryptopp/ccm.h> +#include <cryptopp/filters.h> +#include <cryptopp/md5.h> +#include <cryptopp/modes.h> +#include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_data.h" +#include "core/hw/aes/key.h" + +namespace Service { +namespace NWM { + +using MacAddress = std::array<u8, 6>; + +/* + * Generates a SNAP-enabled 802.2 LLC header for the specified protocol. + * @returns a buffer with the bytes of the generated header. + */ +static std::vector<u8> GenerateLLCHeader(EtherType protocol) { + LLCHeader header{}; + header.protocol = static_cast<u16>(protocol); + + std::vector<u8> buffer(sizeof(header)); + memcpy(buffer.data(), &header, sizeof(header)); + + return buffer; +} + +/* + * Generates a Nintendo UDS SecureData header with the specified parameters. + * @returns a buffer with the bytes of the generated header. + */ +static std::vector<u8> GenerateSecureDataHeader(u16 data_size, u8 channel, u16 dest_node_id, + u16 src_node_id, u16 sequence_number) { + SecureDataHeader header{}; + header.protocol_size = data_size + sizeof(SecureDataHeader); + // Note: This size includes everything except the first 4 bytes of the structure, + // reinforcing the hypotheses that the first 4 bytes are actually the header of + // another container protocol. + header.securedata_size = data_size + sizeof(SecureDataHeader) - 4; + // Frames sent by the emulated application are never UDS management frames + header.is_management = 0; + header.data_channel = channel; + header.sequence_number = sequence_number; + header.dest_node_id = dest_node_id; + header.src_node_id = src_node_id; + + std::vector<u8> buffer(sizeof(header)); + memcpy(buffer.data(), &header, sizeof(header)); + + return buffer; +} + +/* + * Calculates the CTR used for the AES-CTR process that calculates + * the CCMP crypto key for data frames. + * @returns The CTR used for data frames crypto key generation. + */ +static std::array<u8, CryptoPP::MD5::DIGESTSIZE> GetDataCryptoCTR(const NetworkInfo& network_info) { + DataFrameCryptoCTR data{}; + + data.host_mac = network_info.host_mac_address; + data.wlan_comm_id = network_info.wlan_comm_id; + data.id = network_info.id; + data.network_id = network_info.network_id; + + std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash; + CryptoPP::MD5().CalculateDigest(hash.data(), reinterpret_cast<u8*>(&data), sizeof(data)); + + return hash; +} + +/* + * Generates the key used for encrypting the 802.11 data frames generated by UDS. + * @returns The key used for data frames crypto. + */ +static std::array<u8, CryptoPP::AES::BLOCKSIZE> GenerateDataCCMPKey( + const std::vector<u8>& passphrase, const NetworkInfo& network_info) { + // Calculate the MD5 hash of the input passphrase. + std::array<u8, CryptoPP::MD5::DIGESTSIZE> passphrase_hash; + CryptoPP::MD5().CalculateDigest(passphrase_hash.data(), passphrase.data(), passphrase.size()); + + std::array<u8, CryptoPP::AES::BLOCKSIZE> ccmp_key; + + // The CCMP key is the result of encrypting the MD5 hash of the passphrase with AES-CTR using + // keyslot 0x2D. + using CryptoPP::AES; + std::array<u8, CryptoPP::MD5::DIGESTSIZE> counter = GetDataCryptoCTR(network_info); + std::array<u8, AES::BLOCKSIZE> key = HW::AES::GetNormalKey(HW::AES::KeySlotID::UDSDataKey); + CryptoPP::CTR_Mode<AES>::Encryption aes; + aes.SetKeyWithIV(key.data(), AES::BLOCKSIZE, counter.data()); + aes.ProcessData(ccmp_key.data(), passphrase_hash.data(), passphrase_hash.size()); + + return ccmp_key; +} + +/* + * Generates the Additional Authenticated Data (AAD) for an UDS 802.11 encrypted data frame. + * @returns a buffer with the bytes of the AAD. + */ +static std::vector<u8> GenerateCCMPAAD(const MacAddress& sender, const MacAddress& receiver, + const MacAddress& bssid, u16 frame_control) { + // Reference: IEEE 802.11-2007 + + // 8.3.3.3.2 Construct AAD (22-30 bytes) + // The AAD is constructed from the MPDU header. The AAD does not include the header Duration + // field, because the Duration field value can change due to normal IEEE 802.11 operation (e.g., + // a rate change during retransmission). For similar reasons, several subfields in the Frame + // Control field are masked to 0. + struct { + u16_be FC; // MPDU Frame Control field + MacAddress A1; + MacAddress A2; + MacAddress A3; + u16_be SC; // MPDU Sequence Control field + } aad_struct{}; + + constexpr u16 AADFrameControlMask = 0x8FC7; + aad_struct.FC = frame_control & AADFrameControlMask; + aad_struct.SC = 0; + + bool to_ds = (frame_control & (1 << 0)) != 0; + bool from_ds = (frame_control & (1 << 1)) != 0; + // In the 802.11 standard, ToDS = 1 and FromDS = 1 is a valid configuration, + // however, the 3DS doesn't seem to transmit frames with such combination. + ASSERT_MSG(to_ds != from_ds, "Invalid combination"); + + // The meaning of the address fields depends on the ToDS and FromDS fields. + if (from_ds) { + aad_struct.A1 = receiver; + aad_struct.A2 = bssid; + aad_struct.A3 = sender; + } + + if (to_ds) { + aad_struct.A1 = bssid; + aad_struct.A2 = sender; + aad_struct.A3 = receiver; + } + + std::vector<u8> aad(sizeof(aad_struct)); + std::memcpy(aad.data(), &aad_struct, sizeof(aad_struct)); + + return aad; +} + +/* + * Decrypts the payload of an encrypted 802.11 data frame using the specified key. + * @returns The decrypted payload. + */ +static std::vector<u8> DecryptDataFrame(const std::vector<u8>& encrypted_payload, + const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key, + const MacAddress& sender, const MacAddress& receiver, + const MacAddress& bssid, u16 sequence_number, + u16 frame_control) { + + // Reference: IEEE 802.11-2007 + + std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control); + + std::vector<u8> packet_number{0, + 0, + 0, + 0, + static_cast<u8>((sequence_number >> 8) & 0xFF), + static_cast<u8>(sequence_number & 0xFF)}; + + // 8.3.3.3.3 Construct CCM nonce (13 bytes) + std::vector<u8> nonce; + nonce.push_back(0); // priority + nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 + nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN + + try { + CryptoPP::CCM<CryptoPP::AES, 8>::Decryption d; + d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); + d.SpecifyDataLengths(aad.size(), encrypted_payload.size() - 8, 0); + + CryptoPP::AuthenticatedDecryptionFilter df( + d, nullptr, CryptoPP::AuthenticatedDecryptionFilter::MAC_AT_END | + CryptoPP::AuthenticatedDecryptionFilter::THROW_EXCEPTION); + // put aad + df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); + + // put cipher with mac + df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, encrypted_payload.data(), + encrypted_payload.size() - 8); + df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, + encrypted_payload.data() + encrypted_payload.size() - 8, 8); + + df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); + df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); + df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); + + int size = df.MaxRetrievable(); + + std::vector<u8> pdata(size); + df.Get(pdata.data(), size); + return pdata; + } catch (CryptoPP::Exception&) { + LOG_ERROR(Service_NWM, "failed to decrypt"); + } + + return {}; +} + +/* + * Encrypts the payload of an 802.11 data frame using the specified key. + * @returns The encrypted payload. + */ +static std::vector<u8> EncryptDataFrame(const std::vector<u8>& payload, + const std::array<u8, CryptoPP::AES::BLOCKSIZE>& ccmp_key, + const MacAddress& sender, const MacAddress& receiver, + const MacAddress& bssid, u16 sequence_number, + u16 frame_control) { + // Reference: IEEE 802.11-2007 + + std::vector<u8> aad = GenerateCCMPAAD(sender, receiver, bssid, frame_control); + + std::vector<u8> packet_number{0, + 0, + 0, + 0, + static_cast<u8>((sequence_number >> 8) & 0xFF), + static_cast<u8>(sequence_number & 0xFF)}; + + // 8.3.3.3.3 Construct CCM nonce (13 bytes) + std::vector<u8> nonce; + nonce.push_back(0); // priority + nonce.insert(nonce.end(), sender.begin(), sender.end()); // Address 2 + nonce.insert(nonce.end(), packet_number.begin(), packet_number.end()); // PN + + try { + CryptoPP::CCM<CryptoPP::AES, 8>::Encryption d; + d.SetKeyWithIV(ccmp_key.data(), ccmp_key.size(), nonce.data(), nonce.size()); + d.SpecifyDataLengths(aad.size(), payload.size(), 0); + + CryptoPP::AuthenticatedEncryptionFilter df(d); + // put aad + df.ChannelPut(CryptoPP::AAD_CHANNEL, aad.data(), aad.size()); + df.ChannelMessageEnd(CryptoPP::AAD_CHANNEL); + + // put plaintext + df.ChannelPut(CryptoPP::DEFAULT_CHANNEL, payload.data(), payload.size()); + df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); + + df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); + + int size = df.MaxRetrievable(); + + std::vector<u8> cipher(size); + df.Get(cipher.data(), size); + return cipher; + } catch (CryptoPP::Exception&) { + LOG_ERROR(Service_NWM, "failed to encrypt"); + } + + return {}; +} + +std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, + u16 src_node, u16 sequence_number) { + std::vector<u8> buffer = GenerateLLCHeader(EtherType::SecureData); + std::vector<u8> securedata_header = + GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); + + buffer.insert(buffer.end(), securedata_header.begin(), securedata_header.end()); + buffer.insert(buffer.end(), data.begin(), data.end()); + return buffer; +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h new file mode 100644 index 000000000..a23520a41 --- /dev/null +++ b/src/core/hle/service/nwm/uds_data.h @@ -0,0 +1,78 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +enum class SAP : u8 { SNAPExtensionUsed = 0xAA }; + +enum class PDUControl : u8 { UnnumberedInformation = 3 }; + +enum class EtherType : u16 { SecureData = 0x876D, EAPoL = 0x888E }; + +/* + * 802.2 header, UDS packets always use SNAP for these headers, + * which means the dsap and ssap are always SNAPExtensionUsed (0xAA) + * and the OUI is always 0. + */ +struct LLCHeader { + u8 dsap = static_cast<u8>(SAP::SNAPExtensionUsed); + u8 ssap = static_cast<u8>(SAP::SNAPExtensionUsed); + u8 control = static_cast<u8>(PDUControl::UnnumberedInformation); + std::array<u8, 3> OUI = {}; + u16_be protocol; +}; + +static_assert(sizeof(LLCHeader) == 8, "LLCHeader has the wrong size"); + +/* + * Nintendo SecureData header, every UDS packet contains one, + * it is used to store metadata about the transmission such as + * the source and destination network node ids. + */ +struct SecureDataHeader { + // TODO(Subv): It is likely that the first 4 bytes of this header are + // actually part of another container protocol. + u16_be protocol_size; + INSERT_PADDING_BYTES(2); + u16_be securedata_size; + u8 is_management; + u8 data_channel; + u16_be sequence_number; + u16_be dest_node_id; + u16_be src_node_id; +}; + +static_assert(sizeof(SecureDataHeader) == 14, "SecureDataHeader has the wrong size"); + +/* + * The raw bytes of this structure are the CTR used in the encryption (AES-CTR) + * process used to generate the CCMP key for data frame encryption. + */ +struct DataFrameCryptoCTR { + u32_le wlan_comm_id; + u32_le network_id; + std::array<u8, 6> host_mac; + u16_le id; +}; + +static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); + +/** + * Generates an unencrypted 802.11 data payload. + * @returns The generated frame payload. + */ +std::vector<u8> GenerateDataPayload(const std::vector<u8>& data, u8 channel, u16 dest_node, + u16 src_node, u16 sequence_number); + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index 39382ef09..a0b959797 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -152,7 +152,7 @@ void Init() { auto gamecoin_result = Service::FS::OpenFileFromArchive(*archive_result, gamecoin_path, open_mode); if (gamecoin_result.Succeeded()) { - auto gamecoin = gamecoin_result.MoveFrom(); + auto gamecoin = std::move(gamecoin_result).Unwrap(); gamecoin->backend->Write(0, sizeof(GameCoin), true, reinterpret_cast<const u8*>(&default_game_coin)); gamecoin->backend->Close(); diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index 791a65c19..aad950e50 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -173,8 +173,7 @@ void ServiceFrameworkBase::HandleSyncRequest(SharedPtr<ServerSession> server_ses // TODO(yuriks): The kernel should be the one handling this as part of translation after // everything else is migrated - Kernel::HLERequestContext context; - context.session = std::move(server_session); + Kernel::HLERequestContext context(std::move(server_session)); context.PopulateFromIncomingCommandBuffer(cmd_buf, *Kernel::g_current_process, Kernel::g_handle_table); @@ -207,7 +206,7 @@ void AddService(Interface* interface_) { auto server_port = SM::g_service_manager ->RegisterService(interface_->GetPortName(), interface_->GetMaxSessions()) - .MoveFrom(); + .Unwrap(); server_port->SetHleHandler(std::shared_ptr<Interface>(interface_)); } diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 74a1256e0..352941e69 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -113,13 +113,13 @@ void SRV::GetServiceHandle(Kernel::HLERequestContext& ctx) { (*session)->GetObjectId()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(session.Code()); - rb.PushObjects(session.MoveFrom()); + rb.PushObjects(std::move(session).Unwrap()); } else if (session.Code() == Kernel::ERR_MAX_CONNECTIONS_REACHED && return_port_on_failure) { LOG_WARNING(Service_SRV, "called service=%s -> ERR_MAX_CONNECTIONS_REACHED, *port*=%u", name.c_str(), (*client_port)->GetObjectId()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(ERR_MAX_CONNECTIONS_REACHED); - rb.PushObjects(client_port.MoveFrom()); + rb.PushObjects(std::move(client_port).Unwrap()); } else { LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), session.Code()); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); diff --git a/src/core/hle/service/y2r_u.cpp b/src/core/hle/service/y2r_u.cpp index bb7bf2d67..e73971d5f 100644 --- a/src/core/hle/service/y2r_u.cpp +++ b/src/core/hle/service/y2r_u.cpp @@ -275,7 +275,7 @@ static void GetTransferEndEvent(Interface* self) { cmd_buff[0] = IPC::MakeHeader(0xF, 2, 0); cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).MoveFrom(); + cmd_buff[3] = Kernel::g_handle_table.Create(completion_event).Unwrap(); LOG_DEBUG(Service_Y2R, "called"); } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 0a7f011f3..e4b803046 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -37,8 +37,9 @@ //////////////////////////////////////////////////////////////////////////////////////////////////// // Namespace SVC -using Kernel::SharedPtr; using Kernel::ERR_INVALID_HANDLE; +using Kernel::Handle; +using Kernel::SharedPtr; namespace SVC { @@ -1040,7 +1041,6 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client using Kernel::ServerPort; using Kernel::ClientPort; - using Kernel::SharedPtr; auto ports = ServerPort::CreatePortPair(max_sessions); CASCADE_RESULT(*client_port, Kernel::g_handle_table.Create( @@ -1054,6 +1054,41 @@ static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client return RESULT_SUCCESS; } +static ResultCode CreateSessionToPort(Handle* out_client_session, Handle client_port_handle) { + using Kernel::ClientPort; + SharedPtr<ClientPort> client_port = Kernel::g_handle_table.Get<ClientPort>(client_port_handle); + if (client_port == nullptr) + return ERR_INVALID_HANDLE; + + CASCADE_RESULT(auto session, client_port->Connect()); + CASCADE_RESULT(*out_client_session, Kernel::g_handle_table.Create(std::move(session))); + return RESULT_SUCCESS; +} + +static ResultCode CreateSession(Handle* server_session, Handle* client_session) { + auto sessions = Kernel::ServerSession::CreateSessionPair(); + + auto& server = std::get<SharedPtr<Kernel::ServerSession>>(sessions); + CASCADE_RESULT(*server_session, Kernel::g_handle_table.Create(std::move(server))); + + auto& client = std::get<SharedPtr<Kernel::ClientSession>>(sessions); + CASCADE_RESULT(*client_session, Kernel::g_handle_table.Create(std::move(client))); + + LOG_TRACE(Kernel_SVC, "called"); + return RESULT_SUCCESS; +} + +static ResultCode AcceptSession(Handle* out_server_session, Handle server_port_handle) { + using Kernel::ServerPort; + SharedPtr<ServerPort> server_port = Kernel::g_handle_table.Get<ServerPort>(server_port_handle); + if (server_port == nullptr) + return ERR_INVALID_HANDLE; + + CASCADE_RESULT(auto session, server_port->Accept()); + CASCADE_RESULT(*out_server_session, Kernel::g_handle_table.Create(std::move(session))); + return RESULT_SUCCESS; +} + static ResultCode GetSystemInfo(s64* out, u32 type, s32 param) { using Kernel::MemoryRegion; @@ -1228,9 +1263,9 @@ static const FunctionDef SVC_Table[] = { {0x45, nullptr, "Unknown"}, {0x46, nullptr, "Unknown"}, {0x47, HLE::Wrap<CreatePort>, "CreatePort"}, - {0x48, nullptr, "CreateSessionToPort"}, - {0x49, nullptr, "CreateSession"}, - {0x4A, nullptr, "AcceptSession"}, + {0x48, HLE::Wrap<CreateSessionToPort>, "CreateSessionToPort"}, + {0x49, HLE::Wrap<CreateSession>, "CreateSession"}, + {0x4A, HLE::Wrap<AcceptSession>, "AcceptSession"}, {0x4B, nullptr, "ReplyAndReceive1"}, {0x4C, nullptr, "ReplyAndReceive2"}, {0x4D, nullptr, "ReplyAndReceive3"}, diff --git a/src/core/hw/aes/key.h b/src/core/hw/aes/key.h index b01d04f13..c9f1342f4 100644 --- a/src/core/hw/aes/key.h +++ b/src/core/hw/aes/key.h @@ -12,6 +12,8 @@ namespace HW { namespace AES { enum KeySlotID : size_t { + // AES Keyslot used to generate the UDS data frame CCMP key. + UDSDataKey = 0x2D, APTWrap = 0x31, MaxKeySlotID = 0x40, diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 42809c731..6838e449c 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -5,6 +5,7 @@ #include <cstring> #include <numeric> #include <type_traits> +#include "common/alignment.h" #include "common/color.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -313,7 +314,7 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { const PAddr src_addr = config.GetPhysicalInputAddress(); const PAddr dst_addr = config.GetPhysicalOutputAddress(); - // TODO: do hwtest with these cases + // TODO: do hwtest with invalid addresses if (!Memory::IsValidPhysicalAddress(src_addr)) { LOG_CRITICAL(HW_GPU, "invalid input address 0x%08X", src_addr); return; @@ -324,31 +325,36 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { return; } - if (config.texture_copy.input_width == 0) { - LOG_CRITICAL(HW_GPU, "zero input width"); + if (VideoCore::g_renderer->Rasterizer()->AccelerateTextureCopy(config)) return; - } - if (config.texture_copy.output_width == 0) { - LOG_CRITICAL(HW_GPU, "zero output width"); + u8* src_pointer = Memory::GetPhysicalPointer(src_addr); + u8* dst_pointer = Memory::GetPhysicalPointer(dst_addr); + + u32 remaining_size = Common::AlignDown(config.texture_copy.size, 16); + + if (remaining_size == 0) { + LOG_CRITICAL(HW_GPU, "zero size. Real hardware freezes on this."); return; } - if (config.texture_copy.size == 0) { - LOG_CRITICAL(HW_GPU, "zero size"); + u32 input_gap = config.texture_copy.input_gap * 16; + u32 output_gap = config.texture_copy.output_gap * 16; + + // Zero gap means contiguous input/output even if width = 0. To avoid infinite loop below, width + // is assigned with the total size if gap = 0. + u32 input_width = input_gap == 0 ? remaining_size : config.texture_copy.input_width * 16; + u32 output_width = output_gap == 0 ? remaining_size : config.texture_copy.output_width * 16; + + if (input_width == 0) { + LOG_CRITICAL(HW_GPU, "zero input width. Real hardware freezes on this."); return; } - if (VideoCore::g_renderer->Rasterizer()->AccelerateTextureCopy(config)) + if (output_width == 0) { + LOG_CRITICAL(HW_GPU, "zero output width. Real hardware freezes on this."); return; - - u8* src_pointer = Memory::GetPhysicalPointer(src_addr); - u8* dst_pointer = Memory::GetPhysicalPointer(dst_addr); - - u32 input_width = config.texture_copy.input_width * 16; - u32 input_gap = config.texture_copy.input_gap * 16; - u32 output_width = config.texture_copy.output_width * 16; - u32 output_gap = config.texture_copy.output_gap * 16; + } size_t contiguous_input_size = config.texture_copy.size / input_width * (input_width + input_gap); @@ -360,7 +366,6 @@ static void TextureCopy(const Regs::DisplayTransferConfig& config) { Memory::RasterizerFlushAndInvalidateRegion(config.GetPhysicalOutputAddress(), static_cast<u32>(contiguous_output_size)); - u32 remaining_size = config.texture_copy.size; u32 remaining_input = input_width; u32 remaining_output = output_width; while (remaining_size > 0) { diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index bdd997b2a..21b127fee 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -225,7 +225,7 @@ struct Regs { INSERT_PADDING_WORDS(0x1); struct { - u32 size; + u32 size; // The lower 4 bits are ignored union { u32 input_size; diff --git a/src/core/memory.h b/src/core/memory.h index 802aa465e..71fb278ad 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -55,8 +55,10 @@ enum : PAddr { /// Main FCRAM FCRAM_PADDR = 0x20000000, - FCRAM_SIZE = 0x08000000, ///< FCRAM size (128MB) + FCRAM_SIZE = 0x08000000, ///< FCRAM size on the Old 3DS (128MB) + FCRAM_N3DS_SIZE = 0x10000000, ///< FCRAM size on the New 3DS (256MB) FCRAM_PADDR_END = FCRAM_PADDR + FCRAM_SIZE, + FCRAM_N3DS_PADDR_END = FCRAM_PADDR + FCRAM_N3DS_SIZE, }; /// Virtual user-space memory regions |