From 5682608df76b614cadff6061cff35dcf100b944b Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 13:01:21 -0500 Subject: Services/APT: Use boost::optional for the APT parameter structure. --- src/core/hle/service/apt/apt.cpp | 46 +++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index df4b5cc3f..e4068926a 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/common_paths.h" #include "common/file_util.h" #include "common/logging/log.h" @@ -44,7 +45,7 @@ static u8 unknown_ns_state_field; static ScreencapPostPermission screen_capture_post_permission; /// Parameter data to be returned in the next call to Glance/ReceiveParameter -static MessageParameter next_parameter; +static boost::optional next_parameter; void SendParameter(const MessageParameter& parameter) { next_parameter = parameter; @@ -227,18 +228,20 @@ void ReceiveParameter(Service::Interface* self) { 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(next_parameter.buffer.size())); // Parameter buffer size + 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(next_parameter->buffer.size())); // Parameter buffer size - rb.PushMoveHandles((next_parameter.object != nullptr) - ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() + rb.PushMoveHandles((next_parameter->object != nullptr) + ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() : 0); - rb.PushStaticBuffer(buffer, static_cast(next_parameter.buffer.size()), 0); - Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); + rb.PushStaticBuffer(buffer, static_cast(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%08zX", app_id, buffer_size); } @@ -258,17 +261,18 @@ void GlanceParameter(Service::Interface* self) { 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(next_parameter.buffer.size())); // Parameter buffer size + 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(next_parameter->buffer.size())); // Parameter buffer size - rb.PushCopyHandles((next_parameter.object != nullptr) - ? Kernel::g_handle_table.Create(next_parameter.object).Unwrap() + rb.PushMoveHandles((next_parameter->object != nullptr) + ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() : 0); - rb.PushStaticBuffer(buffer, static_cast(next_parameter.buffer.size()), 0); - Memory::WriteBlock(buffer, next_parameter.buffer.data(), next_parameter.buffer.size()); + rb.PushStaticBuffer(buffer, static_cast(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%08zX", app_id, buffer_size); } @@ -800,8 +804,10 @@ void Init() { notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); - next_parameter.signal = static_cast(SignalType::Wakeup); - next_parameter.destination_id = 0x300; + // Initialize the parameter to wake up the application. + next_parameter.emplace(); + next_parameter->signal = static_cast(SignalType::Wakeup); + next_parameter->destination_id = static_cast(AppletId::Application); } void Shutdown() { @@ -812,7 +818,7 @@ void Shutdown() { notification_event = nullptr; parameter_event = nullptr; - next_parameter.object = nullptr; + next_parameter = boost::none; HLE::Applets::Shutdown(); } -- cgit v1.2.3 From 2dc720c355dad55a607c1f993fc823cc198bed08 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 13:02:40 -0500 Subject: Services/APT: Use the right error codes in ReceiveParameter and GlanceParameter when the parameter doesn't exist. --- src/core/hle/service/apt/apt.cpp | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index e4068926a..b5748693f 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -227,6 +227,20 @@ void ReceiveParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); + if (!next_parameter) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + if (next_parameter->destination_id != app_id) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status)); + return; + } + IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); rb.Push(RESULT_SUCCESS); // No error @@ -259,6 +273,20 @@ void GlanceParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); + if (!next_parameter) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + if (next_parameter->destination_id != app_id) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::Applet, ErrorSummary::NotFound, + ErrorLevel::Status)); + return; + } + IPC::RequestBuilder rb = rp.MakeBuilder(4, 4); rb.Push(RESULT_SUCCESS); // No error rb.Push(next_parameter->sender_id); -- cgit v1.2.3 From e403638d9b2cbd7f7dbacd14c3c4bf9863bf7b34 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 13:03:28 -0500 Subject: Services/APT: Properly clear the apt parameter after a successful ReceiveParameter call. --- src/core/hle/service/apt/apt.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index b5748693f..b6c013d43 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -257,7 +257,9 @@ void ReceiveParameter(Service::Interface* self) { Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); - LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + // Clear the parameter + next_parameter = boost::none; + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); } void GlanceParameter(Service::Interface* self) { @@ -302,7 +304,11 @@ void GlanceParameter(Service::Interface* self) { Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); - LOG_WARNING(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + // Note: The NS module always clears the DSPSleep and DSPWakeup signals even in GlanceParameter. + if (next_parameter->signal == static_cast(SignalType::DspSleep) || + next_parameter->signal == static_cast(SignalType::DspWakeup)) + next_parameter = boost::none; + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); } void CancelParameter(Service::Interface* self) { -- cgit v1.2.3 From a9bc417f5948dc63f182d31e4ba271aa23efe84d Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 13:09:45 -0500 Subject: Services/APT: Reset the APT parameter inside CancelParameter if the conditions are met. --- src/core/hle/service/apt/apt.cpp | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index b6c013d43..9cfb7f71e 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -314,17 +314,34 @@ void GlanceParameter(Service::Interface* self) { void CancelParameter(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xF, 4, 0); // 0xF0100 - u32 check_sender = rp.Pop(); + bool check_sender = rp.Pop(); u32 sender_appid = rp.Pop(); - u32 check_receiver = rp.Pop(); + bool check_receiver = rp.Pop(); u32 receiver_appid = rp.Pop(); + + bool cancellation_success = true; + + if (!next_parameter) { + cancellation_success = false; + } else { + if (check_sender && next_parameter->sender_id != sender_appid) + cancellation_success = false; + + if (check_receiver && next_parameter->destination_id != receiver_appid) + cancellation_success = false; + } + + if (cancellation_success) + next_parameter = boost::none; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); // No error - rb.Push(true); // Set to Success + rb.Push(cancellation_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); + LOG_DEBUG(Service_APT, "called check_sender=%u, sender_appid=0x%08X, " + "check_receiver=%u, receiver_appid=0x%08X", + check_sender, sender_appid, check_receiver, receiver_appid); } void PrepareToStartApplication(Service::Interface* self) { -- cgit v1.2.3 From 68596a706860f37de876ca070f9de6e664df0d05 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 13:19:55 -0500 Subject: Services/APT: Return the proper error code when calling SendParameter with an outstanding parameter already in memory. --- src/core/hle/service/apt/apt.cpp | 15 +++++++++++---- src/core/hle/service/apt/apt.h | 6 ++++++ 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 9cfb7f71e..987fb0992 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -192,6 +192,13 @@ void SendParameter(Service::Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + // A new parameter can not be sent if the previous one hasn't been consumed yet + if (next_parameter) { + rb.Push(ResultCode(ErrCodes::ParameterPresent, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + if (dest_applet == nullptr) { LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); rb.Push(-1); // TODO(Subv): Find the right error code @@ -208,10 +215,10 @@ void SendParameter(Service::Interface* self) { 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, 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); + LOG_DEBUG(Service_APT, + "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) { diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index ee80926d2..106754853 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -116,6 +116,12 @@ enum class ScreencapPostPermission : u32 { DisableScreenshotPostingToMiiverse = 3 }; +namespace ErrCodes { +enum { + ParameterPresent = 2, +}; +} + /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter); -- cgit v1.2.3 From e59ab7c1d695e3eb303c5dd37cd0fde814657f53 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 15:03:06 -0500 Subject: Service/APT: Log Send/Cancel/Receive/GlanceParameter calls even if they return an error. --- src/core/hle/service/apt/apt.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 987fb0992..aad23e900 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -190,6 +190,11 @@ void SendParameter(Service::Interface* self) { std::shared_ptr dest_applet = HLE::Applets::Applet::Get(static_cast(dst_app_id)); + LOG_DEBUG(Service_APT, + "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); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); // A new parameter can not be sent if the previous one hasn't been consumed yet @@ -214,11 +219,6 @@ void SendParameter(Service::Interface* self) { Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size()); rb.Push(dest_applet->ReceiveParameter(param)); - - LOG_DEBUG(Service_APT, - "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) { @@ -234,6 +234,8 @@ void ReceiveParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + if (!next_parameter) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet, @@ -266,7 +268,6 @@ void ReceiveParameter(Service::Interface* self) { // Clear the parameter next_parameter = boost::none; - LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); } void GlanceParameter(Service::Interface* self) { @@ -282,6 +283,8 @@ void GlanceParameter(Service::Interface* self) { "buffer_size is bigger than the size in the buffer descriptor (0x%08X > 0x%08zX)", buffer_size, static_buff_size); + LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); + if (!next_parameter) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::Applet, @@ -315,7 +318,6 @@ void GlanceParameter(Service::Interface* self) { if (next_parameter->signal == static_cast(SignalType::DspSleep) || next_parameter->signal == static_cast(SignalType::DspWakeup)) next_parameter = boost::none; - LOG_DEBUG(Service_APT, "called app_id=0x%08X, buffer_size=0x%08zX", app_id, buffer_size); } void CancelParameter(Service::Interface* self) { -- cgit v1.2.3 From 941a722ff1d862fa7b0c2ba73ff9c003324da281 Mon Sep 17 00:00:00 2001 From: James Date: Mon, 31 Jul 2017 16:59:34 +1000 Subject: Handle invalid filenames when renaming files/directories --- src/core/file_sys/archive_sdmc.cpp | 41 ++++++++++++++++++++++++++++++++-- src/core/file_sys/savedata_archive.cpp | 41 ++++++++++++++++++++++++++++++++-- 2 files changed, 78 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index 679909d06..fe3dce5d4 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -121,7 +121,25 @@ ResultCode SDMCArchive::DeleteFile(const Path& path) const { } ResultCode SDMCArchive::RenameFile(const Path& src_path, const Path& dest_path) const { - if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) { + const PathParser path_parser_src(src_path); + + // TODO: Verify these return codes with HW + if (!path_parser_src.IsValid()) { + LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const PathParser path_parser_dest(dest_path); + + if (!path_parser_dest.IsValid()) { + LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto src_path_full = path_parser_src.BuildHostPath(mount_point); + const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); + + if (FileUtil::Rename(src_path_full, dest_path_full)) { return RESULT_SUCCESS; } @@ -260,8 +278,27 @@ ResultCode SDMCArchive::CreateDirectory(const Path& path) const { } ResultCode SDMCArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { - if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) + const PathParser path_parser_src(src_path); + + // TODO: Verify these return codes with HW + if (!path_parser_src.IsValid()) { + LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const PathParser path_parser_dest(dest_path); + + if (!path_parser_dest.IsValid()) { + LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto src_path_full = path_parser_src.BuildHostPath(mount_point); + const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); + + if (FileUtil::Rename(src_path_full, dest_path_full)) { return RESULT_SUCCESS; + } // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't // exist or similar. Verify. diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp index f540c4a93..f8f811ba0 100644 --- a/src/core/file_sys/savedata_archive.cpp +++ b/src/core/file_sys/savedata_archive.cpp @@ -106,7 +106,25 @@ ResultCode SaveDataArchive::DeleteFile(const Path& path) const { } ResultCode SaveDataArchive::RenameFile(const Path& src_path, const Path& dest_path) const { - if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) { + const PathParser path_parser_src(src_path); + + // TODO: Verify these return codes with HW + if (!path_parser_src.IsValid()) { + LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const PathParser path_parser_dest(dest_path); + + if (!path_parser_dest.IsValid()) { + LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto src_path_full = path_parser_src.BuildHostPath(mount_point); + const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); + + if (FileUtil::Rename(src_path_full, dest_path_full)) { return RESULT_SUCCESS; } @@ -247,8 +265,27 @@ ResultCode SaveDataArchive::CreateDirectory(const Path& path) const { } ResultCode SaveDataArchive::RenameDirectory(const Path& src_path, const Path& dest_path) const { - if (FileUtil::Rename(mount_point + src_path.AsString(), mount_point + dest_path.AsString())) + const PathParser path_parser_src(src_path); + + // TODO: Verify these return codes with HW + if (!path_parser_src.IsValid()) { + LOG_ERROR(Service_FS, "Invalid src path %s", src_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const PathParser path_parser_dest(dest_path); + + if (!path_parser_dest.IsValid()) { + LOG_ERROR(Service_FS, "Invalid dest path %s", dest_path.DebugStr().c_str()); + return ERROR_INVALID_PATH; + } + + const auto src_path_full = path_parser_src.BuildHostPath(mount_point); + const auto dest_path_full = path_parser_dest.BuildHostPath(mount_point); + + if (FileUtil::Rename(src_path_full, dest_path_full)) { return RESULT_SUCCESS; + } // TODO(yuriks): This code probably isn't right, it'll return a Status even if the file didn't // exist or similar. Verify. -- cgit v1.2.3 From f5cf9960d9eb5ff5afb39c0356f42035e2dd1ccf Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 1 Aug 2017 19:51:44 -0400 Subject: loader: Expose program title. --- src/core/loader/loader.h | 9 +++++++++ src/core/loader/ncch.cpp | 20 ++++++++++++++++++++ src/core/loader/ncch.h | 14 ++------------ 3 files changed, 31 insertions(+), 12 deletions(-) (limited to 'src/core') diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 48bbf687d..e731888a2 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -166,6 +166,15 @@ public: return ResultStatus::ErrorNotImplemented; } + /** + * Get the title of the application + * @param title Reference to store the application title into + * @return ResultStatus result of function + */ + virtual ResultStatus ReadTitle(std::string& title) { + return ResultStatus::ErrorNotImplemented; + } + protected: FileUtil::IOFile file; bool is_loaded = false; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index fc4d14a59..c007069a9 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -4,7 +4,9 @@ #include #include +#include #include +#include #include #include "common/logging/log.h" #include "common/string_util.h" @@ -420,4 +422,22 @@ ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr& romfs_ return ResultStatus::ErrorNotUsed; } +ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) { + std::vector data; + Loader::SMDH smdh; + ReadIcon(data); + + if (!Loader::IsValidSMDH(data)) { + return ResultStatus::ErrorInvalidFormat; + } + + memcpy(&smdh, data.data(), sizeof(Loader::SMDH)); + + const auto& short_title = smdh.GetShortTitle(SMDH::TitleLanguage::English); + auto title_end = std::find(short_title.begin(), short_title.end(), u'\0'); + title = Common::UTF16ToUTF8(std::u16string{short_title.begin(), title_end}); + + return ResultStatus::Success; +} + } // namespace Loader diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 0ebd47fd5..e40cef764 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -191,23 +191,13 @@ public: ResultStatus ReadLogo(std::vector& buffer) override; - /** - * Get the program id of the application - * @param out_program_id Reference to store program id into - * @return ResultStatus result of function - */ ResultStatus ReadProgramId(u64& out_program_id) override; - /** - * Get the RomFS of the application - * @param romfs_file Reference to buffer to store data - * @param offset Offset in the file to the RomFS - * @param size Size of the RomFS in bytes - * @return ResultStatus result of function - */ ResultStatus ReadRomFS(std::shared_ptr& romfs_file, u64& offset, u64& size) override; + ResultStatus ReadTitle(std::string& title) override; + private: /** * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) -- cgit v1.2.3 From 9b8e5bea6619606afe10dfc38b1d8f6a47ba8332 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 1 Aug 2017 19:53:35 -0400 Subject: core: Expose AppLoader as a public interface. --- src/core/core.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/core.h b/src/core/core.h index 4e3b6b409..9805cc694 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -7,6 +7,7 @@ #include #include #include "common/common_types.h" +#include "core/loader/loader.h" #include "core/memory.h" #include "core/perf_stats.h" #include "core/telemetry_session.h" @@ -14,10 +15,6 @@ class EmuWindow; class ARM_Interface; -namespace Loader { -class AppLoader; -} - namespace Core { class System { @@ -119,6 +116,10 @@ public: return status_details; } + Loader::AppLoader& GetAppLoader() const { + return *app_loader; + } + private: /** * Initialize the emulated system. -- cgit v1.2.3 From a621ab68537bec0d9b3e49d29253fa378636a0a1 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 1 Aug 2017 19:55:48 -0400 Subject: telemetry_session: Log BuildDate and ProgramName fields. --- src/core/telemetry_session.cpp | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/core') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 841d6cfa1..22462f820 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -7,6 +7,7 @@ #include "common/assert.h" #include "common/scm_rev.h" #include "common/x64/cpu_detect.h" +#include "core/core.h" #include "core/settings.h" #include "core/telemetry_session.h" @@ -39,12 +40,18 @@ TelemetrySession::TelemetrySession() { std::chrono::system_clock::now().time_since_epoch()) .count()}; AddField(Telemetry::FieldType::Session, "Init_Time", init_time); + std::string program_name; + const Loader::ResultStatus res{System::GetInstance().GetAppLoader().ReadTitle(program_name)}; + if (res == Loader::ResultStatus::Success) { + AddField(Telemetry::FieldType::Session, "ProgramName", program_name); + } // Log application information const bool is_git_dirty{std::strstr(Common::g_scm_desc, "dirty") != nullptr}; AddField(Telemetry::FieldType::App, "Git_IsDirty", is_git_dirty); AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch); AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev); + AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date); // Log user system information AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); -- cgit v1.2.3 From 5c631ec9c50482e8b34d0a6f3496b5beb1648230 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 1 Aug 2017 20:00:40 -0400 Subject: telemetry: Add field for RequiresSharedFont. --- src/core/hle/service/apt/apt.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 4e6b7b6f5..0109fa2b2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -75,6 +75,10 @@ void Initialize(Service::Interface* self) { void GetSharedFont(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); + + // Log in telemetry if the game uses the shared font + Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); + if (!shared_font_loaded) { LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); rb.Push(-1); // TODO: Find the right error code -- cgit v1.2.3 From 9390d54bb3fe3884a37c4d9698d448106bb30a4e Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 2 Aug 2017 22:47:50 -0400 Subject: telemetry: Add field for BuildName. --- src/core/telemetry_session.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'src/core') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 22462f820..aca513623 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -52,6 +52,7 @@ TelemetrySession::TelemetrySession() { AddField(Telemetry::FieldType::App, "Git_Branch", Common::g_scm_branch); AddField(Telemetry::FieldType::App, "Git_Revision", Common::g_scm_rev); AddField(Telemetry::FieldType::App, "BuildDate", Common::g_build_date); + AddField(Telemetry::FieldType::App, "BuildName", Common::g_build_name); // Log user system information AddField(Telemetry::FieldType::UserSystem, "CPU_Model", Common::GetCPUCaps().cpu_string); -- cgit v1.2.3 From fb8de8985930bc76d8d39d0ff97f880798d5761a Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 2 Aug 2017 23:11:54 -0400 Subject: telemetry: Add field for OsPlatform. --- src/core/telemetry_session.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/core') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index aca513623..94483f385 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -76,6 +76,15 @@ TelemetrySession::TelemetrySession() { Common::GetCPUCaps().sse4_1); AddField(Telemetry::FieldType::UserSystem, "CPU_Extension_x64_SSE42", Common::GetCPUCaps().sse4_2); +#ifdef __APPLE__ + AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Apple"); +#elif defined(_WIN32) + AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Windows"); +#elif defined(__linux__) || defined(linux) || defined(__linux) + AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Linux"); +#else + AddField(Telemetry::FieldType::UserSystem, "OsPlatform", "Unknown"); +#endif // Log user configuration information AddField(Telemetry::FieldType::UserConfig, "Audio_EnableAudioStretching", -- cgit v1.2.3 From 73fba0de46aef0b18ef0ef5221cc23a60be4cb6c Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 22 Jul 2017 12:33:03 -0500 Subject: Services/APT: Use an array to hold data about the 4 possible concurrent applet types (Application, Library, HomeMenu, System). This gives each applet type its own set of events as per the real NS module. --- src/core/hle/service/apt/apt.cpp | 233 +++++++++++++++++++++++++++++++++------ src/core/hle/service/apt/apt.h | 6 +- 2 files changed, 204 insertions(+), 35 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 0109fa2b2..9cfa9efde 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -34,8 +34,6 @@ static bool shared_font_loaded = false; static bool shared_font_relocated = false; static Kernel::SharedPtr lock; -static Kernel::SharedPtr notification_event; ///< APT notification event -static Kernel::SharedPtr parameter_event; ///< APT parameter event static u32 cpu_percent; ///< CPU time available to the running application @@ -44,32 +42,164 @@ static u8 unknown_ns_state_field; static ScreencapPostPermission screen_capture_post_permission; -/// Parameter data to be returned in the next call to Glance/ReceiveParameter +/// Parameter data to be returned in the next call to Glance/ReceiveParameter. +/// TODO(Subv): Use std::optional once we migrate to C++17. static boost::optional next_parameter; +enum class AppletPos { Application = 0, Library = 1, System = 2, SysLibrary = 3, Resident = 4 }; + +static constexpr size_t NumAppletSlot = 4; + +enum class AppletSlot : u8 { + Application, + SystemApplet, + HomeMenu, + LibraryApplet, + + // An invalid tag + Error, +}; + +struct AppletSlotData { + AppletId applet_id; + AppletSlot slot; + bool registered; + u32 attributes; + Kernel::SharedPtr notification_event; + Kernel::SharedPtr parameter_event; +}; + +// Holds data about the concurrently running applets in the system. +static std::array applet_slots = {}; + +union AppletAttributes { + u32 raw; + + BitField<0, 3, u32> applet_pos; + + AppletAttributes(u32 attributes) : raw(attributes) {} +}; + +// Helper function to extract the AppletPos from the lower bits of the applet attributes +static u32 GetAppletPos(AppletAttributes attributes) { + return attributes.applet_pos; +} + +// This overload returns nullptr if no applet with the specified id has been started. +static AppletSlotData* GetAppletSlotData(AppletId id) { + auto GetSlot = [](AppletSlot slot) -> AppletSlotData* { + return &applet_slots[static_cast(slot)]; + }; + + if (id == AppletId::Application) { + auto* slot = GetSlot(AppletSlot::Application); + if (slot->applet_id != AppletId::None) + return slot; + + return nullptr; + } + + if (id == AppletId::AnySystemApplet) { + auto* system_slot = GetSlot(AppletSlot::SystemApplet); + if (system_slot->applet_id != AppletId::None) + return system_slot; + + // The Home Menu is also a system applet, but it lives in its own slot to be able to run + // concurrently with other system applets. + auto* home_slot = GetSlot(AppletSlot::HomeMenu); + if (home_slot->applet_id != AppletId::None) + return home_slot; + + return nullptr; + } + + if (id == AppletId::AnyLibraryApplet || id == AppletId::AnySysLibraryApplet) { + auto* slot = GetSlot(AppletSlot::LibraryApplet); + if (slot->applet_id == AppletId::None) + return nullptr; + + u32 applet_pos = GetAppletPos(slot->attributes); + + if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast(AppletPos::Library)) + return slot; + + if (id == AppletId::AnySysLibraryApplet && + applet_pos == static_cast(AppletPos::SysLibrary)) + return slot; + + return nullptr; + } + + if (id == AppletId::HomeMenu || id == AppletId::AlternateMenu) { + auto* slot = GetSlot(AppletSlot::HomeMenu); + if (slot->applet_id != AppletId::None) + return slot; + + return nullptr; + } + + for (auto& slot : applet_slots) { + if (slot.applet_id == id) + return &slot; + } + + return nullptr; +} + +static AppletSlotData* GetAppletSlotData(u32 attributes) { + // Mapping from AppletPos to AppletSlot + static constexpr std::array applet_position_slots = { + AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet, + AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet}; + + u32 applet_pos = GetAppletPos(attributes); + if (applet_pos >= applet_position_slots.size()) + return nullptr; + + AppletSlot slot = applet_position_slots[applet_pos]; + + if (slot == AppletSlot::Error) + return nullptr; + + return &applet_slots[static_cast(slot)]; +} + void SendParameter(const MessageParameter& parameter) { next_parameter = parameter; - // Signal the event to let the application know that a new parameter is ready to be read - parameter_event->Signal(); + // Signal the event to let the receiver know that a new parameter is ready to be read + auto* const slot_data = GetAppletSlotData(static_cast(parameter.destination_id)); + ASSERT(slot_data); + + slot_data->parameter_event->Signal(); } void Initialize(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x2, 2, 0); // 0x20080 u32 app_id = rp.Pop(); - u32 flags = rp.Pop(); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); - rb.Push(RESULT_SUCCESS); - rb.PushCopyHandles(Kernel::g_handle_table.Create(notification_event).Unwrap(), - Kernel::g_handle_table.Create(parameter_event).Unwrap()); + u32 attributes = rp.Pop(); + + LOG_DEBUG(Service_APT, "called app_id=0x%08X, attributes=0x%08X", app_id, attributes); + + auto* const slot_data = GetAppletSlotData(attributes); + + // Note: The real NS service does not check if the attributes value is valid before accessing + // the data in the array + ASSERT_MSG(slot_data, "Invalid application attributes"); - // TODO(bunnei): Check if these events are cleared every time Initialize is called. - notification_event->Clear(); - parameter_event->Clear(); + if (slot_data->registered) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } - ASSERT_MSG((nullptr != lock), "Cannot initialize without lock"); - lock->Release(); + slot_data->applet_id = static_cast(app_id); + slot_data->attributes = attributes; - LOG_DEBUG(Service_APT, "called app_id=0x%08X, flags=0x%08X", app_id, flags); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), + Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); } void GetSharedFont(Service::Interface* self) { @@ -120,7 +250,12 @@ void GetLockHandle(Service::Interface* self) { // this will cause the app to wait until parameter_event is signaled. u32 applet_attributes = rp.Pop(); IPC::RequestBuilder rb = rp.MakeBuilder(3, 2); - rb.Push(RESULT_SUCCESS); // No error + rb.Push(RESULT_SUCCESS); // No error + + // TODO(Subv): The output attributes should have an AppletPos of either Library or System | + // Library (depending on the type of the last launched applet) if the input attributes' + // AppletPos has the Library bit set. + rb.Push(applet_attributes); // Applet Attributes, this value is passed to Enable. rb.Push(0); // Least significant bit = power button state Kernel::Handle handle_copy = Kernel::g_handle_table.Create(lock).Unwrap(); @@ -133,10 +268,22 @@ void GetLockHandle(Service::Interface* self) { void Enable(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x3, 1, 0); // 0x30040 u32 attributes = rp.Pop(); + + LOG_DEBUG(Service_APT, "called attributes=0x%08X", attributes); + 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); + + auto* const slot_data = GetAppletSlotData(attributes); + + if (!slot_data) { + rb.Push(ResultCode(ErrCodes::InvalidAppletSlot, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + return; + } + + slot_data->registered = true; + + rb.Push(RESULT_SUCCESS); } void GetAppletManInfo(Service::Interface* self) { @@ -154,22 +301,27 @@ void GetAppletManInfo(Service::Interface* self) { void IsRegistered(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 1, 0); // 0x90040 - u32 app_id = rp.Pop(); + AppletId app_id = static_cast(rp.Pop()); 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. - bool is_registered = false; // Set to not registered by default + auto* const slot_data = GetAppletSlotData(app_id); + + // Check if an LLE applet was registered first, then fallback to HLE applets + bool is_registered = slot_data && slot_data->registered; - if (app_id == static_cast(AppletId::AnyLibraryApplet)) { - is_registered = HLE::Applets::IsLibraryAppletRunning(); - } else if (auto applet = HLE::Applets::Applet::Get(static_cast(app_id))) { - is_registered = true; // Set to registered + if (!is_registered) { + if (app_id == AppletId::AnyLibraryApplet) { + is_registered = HLE::Applets::IsLibraryAppletRunning(); + } else if (auto applet = HLE::Applets::Applet::Get(app_id)) { + // The applet exists, set it as registered. + is_registered = true; + } } + rb.Push(is_registered); - LOG_WARNING(Service_APT, "(STUBBED) called app_id=0x%08X", app_id); + LOG_DEBUG(Service_APT, "called app_id=0x%08X", static_cast(app_id)); } void InquireNotification(Service::Interface* self) { @@ -864,14 +1016,23 @@ void Init() { screen_capture_post_permission = ScreencapPostPermission::CleanThePermission; // TODO(JamePeng): verify the initial value - // TODO(bunnei): Check if these are created in Initialize or on APT process startup. - notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Notification"); - parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT_U:Start"); + for (size_t slot = 0; slot < applet_slots.size(); ++slot) { + auto& slot_data = applet_slots[slot]; + slot_data.slot = static_cast(slot); + slot_data.applet_id = AppletId::None; + slot_data.attributes = 0; + slot_data.registered = false; + slot_data.notification_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification"); + slot_data.parameter_event = + Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); + } // Initialize the parameter to wake up the application. next_parameter.emplace(); next_parameter->signal = static_cast(SignalType::Wakeup); next_parameter->destination_id = static_cast(AppletId::Application); + applet_slots[static_cast(AppletSlot::Application)].parameter_event->Signal(); } void Shutdown() { @@ -879,8 +1040,12 @@ void Shutdown() { shared_font_loaded = false; shared_font_relocated = false; lock = nullptr; - notification_event = nullptr; - parameter_event = nullptr; + + for (auto& slot : applet_slots) { + slot.registered = false; + slot.notification_event = nullptr; + slot.parameter_event = nullptr; + } next_parameter = boost::none; diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 106754853..96b28b438 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -72,6 +72,8 @@ enum class SignalType : u32 { /// App Id's used by APT functions enum class AppletId : u32 { + None = 0, + AnySystemApplet = 0x100, HomeMenu = 0x101, AlternateMenu = 0x103, Camera = 0x110, @@ -83,6 +85,7 @@ enum class AppletId : u32 { Miiverse = 0x117, MiiversePost = 0x118, AmiiboSettings = 0x119, + AnySysLibraryApplet = 0x200, SoftwareKeyboard1 = 0x201, Ed1 = 0x202, PnoteApp = 0x204, @@ -119,8 +122,9 @@ enum class ScreencapPostPermission : u32 { namespace ErrCodes { enum { ParameterPresent = 2, + InvalidAppletSlot = 4, }; -} +} // namespace ErrCodes /// Send a parameter to the currently-running application, which will read it via ReceiveParameter void SendParameter(const MessageParameter& parameter); -- cgit v1.2.3 From 177e8ce655953e22b5304070694f2d2d6e65dda9 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 7 Aug 2017 16:09:55 -0500 Subject: Services/APT: Use the AppletAttributes union directly when dealing with applet attrs. --- src/core/hle/service/apt/apt.cpp | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 9cfa9efde..58d94768c 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -60,11 +60,20 @@ enum class AppletSlot : u8 { Error, }; +union AppletAttributes { + u32 raw; + + BitField<0, 3, u32> applet_pos; + + AppletAttributes() : raw(0) {} + AppletAttributes(u32 attributes) : raw(attributes) {} +}; + struct AppletSlotData { AppletId applet_id; AppletSlot slot; bool registered; - u32 attributes; + AppletAttributes attributes; Kernel::SharedPtr notification_event; Kernel::SharedPtr parameter_event; }; @@ -72,19 +81,6 @@ struct AppletSlotData { // Holds data about the concurrently running applets in the system. static std::array applet_slots = {}; -union AppletAttributes { - u32 raw; - - BitField<0, 3, u32> applet_pos; - - AppletAttributes(u32 attributes) : raw(attributes) {} -}; - -// Helper function to extract the AppletPos from the lower bits of the applet attributes -static u32 GetAppletPos(AppletAttributes attributes) { - return attributes.applet_pos; -} - // This overload returns nullptr if no applet with the specified id has been started. static AppletSlotData* GetAppletSlotData(AppletId id) { auto GetSlot = [](AppletSlot slot) -> AppletSlotData* { @@ -118,7 +114,7 @@ static AppletSlotData* GetAppletSlotData(AppletId id) { if (slot->applet_id == AppletId::None) return nullptr; - u32 applet_pos = GetAppletPos(slot->attributes); + u32 applet_pos = slot->attributes.applet_pos; if (id == AppletId::AnyLibraryApplet && applet_pos == static_cast(AppletPos::Library)) return slot; @@ -146,13 +142,13 @@ static AppletSlotData* GetAppletSlotData(AppletId id) { return nullptr; } -static AppletSlotData* GetAppletSlotData(u32 attributes) { +static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { // Mapping from AppletPos to AppletSlot static constexpr std::array applet_position_slots = { AppletSlot::Application, AppletSlot::LibraryApplet, AppletSlot::SystemApplet, AppletSlot::LibraryApplet, AppletSlot::Error, AppletSlot::LibraryApplet}; - u32 applet_pos = GetAppletPos(attributes); + u32 applet_pos = attributes.applet_pos; if (applet_pos >= applet_position_slots.size()) return nullptr; @@ -194,7 +190,7 @@ void Initialize(Service::Interface* self) { } slot_data->applet_id = static_cast(app_id); - slot_data->attributes = attributes; + slot_data->attributes.raw = attributes; IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); rb.Push(RESULT_SUCCESS); @@ -1020,7 +1016,7 @@ void Init() { auto& slot_data = applet_slots[slot]; slot_data.slot = static_cast(slot); slot_data.applet_id = AppletId::None; - slot_data.attributes = 0; + slot_data.attributes.raw = 0; slot_data.registered = false; slot_data.notification_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Notification"); -- cgit v1.2.3 From 1a44949ef75016fa48f9daa0cf3c973ef7d3978c Mon Sep 17 00:00:00 2001 From: James Date: Tue, 8 Aug 2017 17:50:09 +1000 Subject: Update cryptopp --- src/core/hle/service/cfg/cfg.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 6624f1711..3dbeb27cc 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -681,7 +681,7 @@ void GenerateConsoleUniqueId(u32& random_number, u64& console_id) { CryptoPP::AutoSeededRandomPool rng; random_number = rng.GenerateWord32(0, 0xFFFF); u64_le local_friend_code_seed; - rng.GenerateBlock(reinterpret_cast(&local_friend_code_seed), + rng.GenerateBlock(reinterpret_cast(&local_friend_code_seed), sizeof(local_friend_code_seed)); console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast(random_number) << 48); } -- cgit v1.2.3 From a6273dd56a4a7066ef27fa186e1046db677ff578 Mon Sep 17 00:00:00 2001 From: mailwl Date: Wed, 9 Aug 2017 12:00:54 +0300 Subject: Service/dlp: Update function tables according 3dbrew --- src/core/hle/service/dlp/dlp_clnt.cpp | 21 ++++++++++++++++++++- src/core/hle/service/dlp/dlp_fkcl.cpp | 18 +++++++++++++++++- src/core/hle/service/dlp/dlp_srvr.cpp | 9 +++++++-- 3 files changed, 44 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/dlp/dlp_clnt.cpp b/src/core/hle/service/dlp/dlp_clnt.cpp index 56f934b3f..6f2bf2061 100644 --- a/src/core/hle/service/dlp/dlp_clnt.cpp +++ b/src/core/hle/service/dlp/dlp_clnt.cpp @@ -8,7 +8,26 @@ namespace Service { namespace DLP { const Interface::FunctionInfo FunctionTable[] = { - {0x000100C3, nullptr, "Initialize"}, {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, + {0x000100C3, nullptr, "Initialize"}, + {0x00020000, nullptr, "Finalize"}, + {0x00030000, nullptr, "GetEventDesc"}, + {0x00040000, nullptr, "GetChannel"}, + {0x00050180, nullptr, "StartScan"}, + {0x00060000, nullptr, "StopScan"}, + {0x00070080, nullptr, "GetServerInfo"}, + {0x00080100, nullptr, "GetTitleInfo"}, + {0x00090040, nullptr, "GetTitleInfoInOrder"}, + {0x000A0080, nullptr, "DeleteScanInfo"}, + {0x000B0100, nullptr, "PrepareForSystemDownload"}, + {0x000C0000, nullptr, "StartSystemDownload"}, + {0x000D0100, nullptr, "StartTitleDownload"}, + {0x000E0000, nullptr, "GetMyStatus"}, + {0x000F0040, nullptr, "GetConnectingNodes"}, + {0x00100040, nullptr, "GetNodeInfo"}, + {0x00110000, nullptr, "GetWirelessRebootPassphrase"}, + {0x00120000, nullptr, "StopSession"}, + {0x00130100, nullptr, "GetCupVersion"}, + {0x00140100, nullptr, "GetDupAvailability"}, }; DLP_CLNT_Interface::DLP_CLNT_Interface() { diff --git a/src/core/hle/service/dlp/dlp_fkcl.cpp b/src/core/hle/service/dlp/dlp_fkcl.cpp index 29b9d52e0..fe6be7d32 100644 --- a/src/core/hle/service/dlp/dlp_fkcl.cpp +++ b/src/core/hle/service/dlp/dlp_fkcl.cpp @@ -8,7 +8,23 @@ namespace Service { namespace DLP { const Interface::FunctionInfo FunctionTable[] = { - {0x00010083, nullptr, "Initialize"}, {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, + {0x00010083, nullptr, "Initialize"}, + {0x00020000, nullptr, "Finalize"}, + {0x00030000, nullptr, "GetEventDesc"}, + {0x00040000, nullptr, "GetChannels"}, + {0x00050180, nullptr, "StartScan"}, + {0x00060000, nullptr, "StopScan"}, + {0x00070080, nullptr, "GetServerInfo"}, + {0x00080100, nullptr, "GetTitleInfo"}, + {0x00090040, nullptr, "GetTitleInfoInOrder"}, + {0x000A0080, nullptr, "DeleteScanInfo"}, + {0x000B0100, nullptr, "StartFakeSession"}, + {0x000C0000, nullptr, "GetMyStatus"}, + {0x000D0040, nullptr, "GetConnectingNodes"}, + {0x000E0040, nullptr, "GetNodeInfo"}, + {0x000F0000, nullptr, "GetWirelessRebootPassphrase"}, + {0x00100000, nullptr, "StopSession"}, + {0x00110203, nullptr, "Initialize2"}, }; DLP_FKCL_Interface::DLP_FKCL_Interface() { diff --git a/src/core/hle/service/dlp/dlp_srvr.cpp b/src/core/hle/service/dlp/dlp_srvr.cpp index 32cfa2c44..1bcea43d3 100644 --- a/src/core/hle/service/dlp/dlp_srvr.cpp +++ b/src/core/hle/service/dlp/dlp_srvr.cpp @@ -11,7 +11,7 @@ namespace Service { namespace DLP { -static void unk_0x000E0040(Interface* self) { +static void IsChild(Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); cmd_buff[1] = RESULT_SUCCESS.raw; @@ -24,14 +24,19 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00010183, nullptr, "Initialize"}, {0x00020000, nullptr, "Finalize"}, {0x00030000, nullptr, "GetServerState"}, + {0x00040000, nullptr, "GetEventDescription"}, {0x00050080, nullptr, "StartAccepting"}, + {0x00060000, nullptr, "EndAccepting"}, {0x00070000, nullptr, "StartDistribution"}, {0x000800C0, nullptr, "SendWirelessRebootPassphrase"}, {0x00090040, nullptr, "AcceptClient"}, + {0x000A0040, nullptr, "DisconnectClient"}, {0x000B0042, nullptr, "GetConnectingClients"}, {0x000C0040, nullptr, "GetClientInfo"}, {0x000D0040, nullptr, "GetClientState"}, - {0x000E0040, unk_0x000E0040, "unk_0x000E0040"}, + {0x000E0040, IsChild, "IsChild"}, + {0x000F0303, nullptr, "InitializeWithName"}, + {0x00100000, nullptr, "GetDupNoticeNeed"}, }; DLP_SRVR_Interface::DLP_SRVR_Interface() { -- cgit v1.2.3 From 599de29ea345bbeee300bec4bd6c3ab31288268d Mon Sep 17 00:00:00 2001 From: wwylele Date: Wed, 9 Aug 2017 06:59:13 +0300 Subject: HID: zero unused PadState bits --- src/core/hle/service/hid/hid.h | 2 +- src/core/hle/service/ir/ir_rst.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index 1ef972e70..ef25926b5 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -24,7 +24,7 @@ namespace HID { */ struct PadState { union { - u32 hex; + u32 hex{}; BitField<0, 1, u32> a; BitField<1, 1, u32> b; diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 837413f93..0912d5756 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -18,7 +18,7 @@ namespace Service { namespace IR { union PadState { - u32_le hex; + u32_le hex{}; BitField<14, 1, u32_le> zl; BitField<15, 1, u32_le> zr; -- cgit v1.2.3 From 867eabd6b7effc8ba6c21d7fcbd0480b14f81ad0 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 6 Aug 2017 22:54:19 +0300 Subject: HID: use MotionDevice for Accelerometer and Gyroscope --- src/core/frontend/input.h | 20 ++++++++++++++++++++ src/core/hle/service/hid/hid.cpp | 32 +++++++++++++++++++++++++++----- src/core/settings.h | 1 + 3 files changed, 48 insertions(+), 5 deletions(-) (limited to 'src/core') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 0a5713dc0..a8be49440 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -11,6 +11,7 @@ #include #include "common/logging/log.h" #include "common/param_package.h" +#include "common/vector_math.h" namespace Input { @@ -107,4 +108,23 @@ using ButtonDevice = InputDevice; */ using AnalogDevice = InputDevice>; +/** + * A motion device is an input device that returns a tuple of accelerometer state vector and + * gyroscope state vector. + * + * For accelerometer state vector: + * x+ is the same direction as LEFT on D-pad. + * y+ is normal to the touch screen, pointing outward. + * z+ is the same direction as UP on D-pad. + * Units: measured in unit of gravitational acceleration + * + * For gyroscope state vector: + * x+ is the same direction as LEFT on D-pad. + * y+ is normal to the touch screen, pointing outward. + * z+ is the same direction as UP on D-pad. + * Orientation is determined by right-hand rule. + * Units: deg/sec + */ +using MotionDevice = InputDevice, Math::Vec3>>; + } // namespace Input diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 2014b8461..a13b72e88 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -7,6 +7,7 @@ #include #include #include "common/logging/log.h" +#include "core/core.h" #include "core/core_timing.h" #include "core/frontend/emu_window.h" #include "core/frontend/input.h" @@ -50,10 +51,14 @@ 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; +constexpr float accelerometer_coef = 512.0f; // measured from hw test result +constexpr float gyroscope_coef = 14.375f; // got from hwtest GetGyroscopeLowRawToDpsCoefficient call + static std::atomic is_device_reload_pending; static std::array, Settings::NativeButton::NUM_BUTTONS_HID> buttons; static std::unique_ptr circle_pad; +static std::unique_ptr motion_device; DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions @@ -90,6 +95,7 @@ static void LoadInputDevices() { buttons.begin(), Input::CreateDevice); circle_pad = Input::CreateDevice( Settings::values.analogs[Settings::NativeAnalog::CirclePad]); + motion_device = Input::CreateDevice(Settings::values.motion_device); } static void UnloadInputDevices() { @@ -97,6 +103,7 @@ static void UnloadInputDevices() { button.reset(); } circle_pad.reset(); + motion_device.reset(); } static void UpdatePadCallback(u64 userdata, int cycles_late) { @@ -193,10 +200,19 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) { mem->accelerometer.index = next_accelerometer_index; next_accelerometer_index = (next_accelerometer_index + 1) % mem->accelerometer.entries.size(); + Math::Vec3 accel; + std::tie(accel, std::ignore) = motion_device->GetStatus(); + accel *= accelerometer_coef; + // TODO(wwylele): do a time stretch as it in UpdateGyroscopeCallback + // The time stretch formula should be like + // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity + AccelerometerDataEntry& accelerometer_entry = mem->accelerometer.entries[mem->accelerometer.index]; - std::tie(accelerometer_entry.x, accelerometer_entry.y, accelerometer_entry.z) = - VideoCore::g_emu_window->GetAccelerometerState(); + + accelerometer_entry.x = static_cast(accel.x); + accelerometer_entry.y = static_cast(accel.y); + accelerometer_entry.z = static_cast(accel.z); // Make up "raw" entry // TODO(wwylele): @@ -227,8 +243,14 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { next_gyroscope_index = (next_gyroscope_index + 1) % mem->gyroscope.entries.size(); GyroscopeDataEntry& gyroscope_entry = mem->gyroscope.entries[mem->gyroscope.index]; - std::tie(gyroscope_entry.x, gyroscope_entry.y, gyroscope_entry.z) = - VideoCore::g_emu_window->GetGyroscopeState(); + + Math::Vec3 gyro; + std::tie(std::ignore, gyro) = motion_device->GetStatus(); + float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); + gyro *= gyroscope_coef * stretch; + gyroscope_entry.x = static_cast(gyro.x); + gyroscope_entry.y = static_cast(gyro.y); + gyroscope_entry.z = static_cast(gyro.z); // Make up "raw" entry mem->gyroscope.raw_entry.x = gyroscope_entry.x; @@ -326,7 +348,7 @@ void GetGyroscopeLowRawToDpsCoefficient(Service::Interface* self) { cmd_buff[1] = RESULT_SUCCESS.raw; - f32 coef = VideoCore::g_emu_window->GetGyroscopeRawToDpsCoefficient(); + f32 coef = gyroscope_coef; memcpy(&cmd_buff[2], &coef, 4); } diff --git a/src/core/settings.h b/src/core/settings.h index ee16bb90a..7e15b119b 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -79,6 +79,7 @@ struct Values { // Controls std::array buttons; std::array analogs; + std::string motion_device; // Core bool use_cpu_jit; -- cgit v1.2.3 From 188194908c2785bd1e03485941b9148777cdddd7 Mon Sep 17 00:00:00 2001 From: wwylele Date: Mon, 7 Aug 2017 00:04:06 +0300 Subject: move MotionEmu from core/frontend to input_common as a InputDevice --- src/core/CMakeLists.txt | 2 - src/core/frontend/emu_window.cpp | 23 ----------- src/core/frontend/emu_window.h | 83 ------------------------------------- src/core/frontend/input.h | 9 ++-- src/core/frontend/motion_emu.cpp | 89 ---------------------------------------- src/core/frontend/motion_emu.h | 52 ----------------------- 6 files changed, 4 insertions(+), 254 deletions(-) delete mode 100644 src/core/frontend/motion_emu.cpp delete mode 100644 src/core/frontend/motion_emu.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..ea36b7d95 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -33,7 +33,6 @@ set(SRCS frontend/camera/interface.cpp frontend/emu_window.cpp frontend/framebuffer_layout.cpp - frontend/motion_emu.cpp gdbstub/gdbstub.cpp hle/config_mem.cpp hle/applets/applet.cpp @@ -226,7 +225,6 @@ set(HEADERS frontend/emu_window.h frontend/framebuffer_layout.h frontend/input.h - frontend/motion_emu.h gdbstub/gdbstub.h hle/config_mem.h hle/function_wrappers.h diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 4f7d54a33..60b20d4e2 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -62,29 +62,6 @@ void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { TouchPressed(framebuffer_x, framebuffer_y); } -void EmuWindow::AccelerometerChanged(float x, float y, float z) { - constexpr float coef = 512; - - std::lock_guard lock(accel_mutex); - - // TODO(wwylele): do a time stretch as it in GyroscopeChanged - // The time stretch formula should be like - // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity - accel_x = static_cast(x * coef); - accel_y = static_cast(y * coef); - accel_z = static_cast(z * coef); -} - -void EmuWindow::GyroscopeChanged(float x, float y, float z) { - constexpr float FULL_FPS = 60; - float coef = GetGyroscopeRawToDpsCoefficient(); - float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); - std::lock_guard lock(gyro_mutex); - gyro_x = static_cast(x * coef * stretch); - gyro_y = static_cast(y * coef * stretch); - gyro_z = static_cast(z * coef * stretch); -} - void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) { Layout::FramebufferLayout layout; if (Settings::values.custom_layout == true) { diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 9414123a4..7bdee251c 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -68,27 +68,6 @@ public: */ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); - /** - * Signal accelerometer state has changed. - * @param x X-axis accelerometer value - * @param y Y-axis accelerometer value - * @param z Z-axis accelerometer value - * @note all values are in unit of g (gravitational acceleration). - * e.g. x = 1.0 means 9.8m/s^2 in x direction. - * @see GetAccelerometerState for axis explanation. - */ - void AccelerometerChanged(float x, float y, float z); - - /** - * Signal gyroscope state has changed. - * @param x X-axis accelerometer value - * @param y Y-axis accelerometer value - * @param z Z-axis accelerometer value - * @note all values are in deg/sec. - * @see GetGyroscopeState for axis explanation. - */ - void GyroscopeChanged(float x, float y, float z); - /** * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). * @note This should be called by the core emu thread to get a state set by the window thread. @@ -100,52 +79,6 @@ public: return std::make_tuple(touch_x, touch_y, touch_pressed); } - /** - * Gets the current accelerometer state (acceleration along each three axis). - * Axis explained: - * +x is the same direction as LEFT on D-pad. - * +y is normal to the touch screen, pointing outward. - * +z is the same direction as UP on D-pad. - * Units: - * 1 unit of return value = 1/512 g (measured by hw test), - * where g is the gravitational acceleration (9.8 m/sec2). - * @note This should be called by the core emu thread to get a state set by the window thread. - * @return std::tuple of (x, y, z) - */ - std::tuple GetAccelerometerState() { - std::lock_guard lock(accel_mutex); - return std::make_tuple(accel_x, accel_y, accel_z); - } - - /** - * Gets the current gyroscope state (angular rates about each three axis). - * Axis explained: - * +x is the same direction as LEFT on D-pad. - * +y is normal to the touch screen, pointing outward. - * +z is the same direction as UP on D-pad. - * Orientation is determined by right-hand rule. - * Units: - * 1 unit of return value = (1/coef) deg/sec, - * where coef is the return value of GetGyroscopeRawToDpsCoefficient(). - * @note This should be called by the core emu thread to get a state set by the window thread. - * @return std::tuple of (x, y, z) - */ - std::tuple GetGyroscopeState() { - std::lock_guard lock(gyro_mutex); - return std::make_tuple(gyro_x, gyro_y, gyro_z); - } - - /** - * Gets the coefficient for units conversion of gyroscope state. - * The conversion formula is r = coefficient * v, - * where v is angular rate in deg/sec, - * and r is the gyroscope state. - * @return float-type coefficient - */ - f32 GetGyroscopeRawToDpsCoefficient() const { - return 14.375f; // taken from hw test, and gyroscope's document - } - /** * Returns currently active configuration. * @note Accesses to the returned object need not be consistent because it may be modified in @@ -187,12 +120,6 @@ protected: touch_x = 0; touch_y = 0; touch_pressed = false; - accel_x = 0; - accel_y = -512; - accel_z = 0; - gyro_x = 0; - gyro_y = 0; - gyro_z = 0; } virtual ~EmuWindow() {} @@ -255,16 +182,6 @@ private: u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) - std::mutex accel_mutex; - s16 accel_x; ///< Accelerometer X-axis value in native 3DS units - s16 accel_y; ///< Accelerometer Y-axis value in native 3DS units - s16 accel_z; ///< Accelerometer Z-axis value in native 3DS units - - std::mutex gyro_mutex; - s16 gyro_x; ///< Gyroscope X-axis value in native 3DS units - s16 gyro_y; ///< Gyroscope Y-axis value in native 3DS units - s16 gyro_z; ///< Gyroscope Z-axis value in native 3DS units - /** * Clip the provided coordinates to be inside the touchscreen area. */ diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index a8be49440..5916a901d 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -112,16 +112,15 @@ using AnalogDevice = InputDevice>; * A motion device is an input device that returns a tuple of accelerometer state vector and * gyroscope state vector. * - * For accelerometer state vector: + * For both vectors: * x+ is the same direction as LEFT on D-pad. * y+ is normal to the touch screen, pointing outward. * z+ is the same direction as UP on D-pad. - * Units: measured in unit of gravitational acceleration + * + * For accelerometer state vector + * Units: g (gravitational acceleration) * * For gyroscope state vector: - * x+ is the same direction as LEFT on D-pad. - * y+ is normal to the touch screen, pointing outward. - * z+ is the same direction as UP on D-pad. * Orientation is determined by right-hand rule. * Units: deg/sec */ diff --git a/src/core/frontend/motion_emu.cpp b/src/core/frontend/motion_emu.cpp deleted file mode 100644 index 9a5b3185d..000000000 --- a/src/core/frontend/motion_emu.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "common/math_util.h" -#include "common/quaternion.h" -#include "core/frontend/emu_window.h" -#include "core/frontend/motion_emu.h" - -namespace Motion { - -static constexpr int update_millisecond = 100; -static constexpr auto update_duration = - std::chrono::duration_cast( - std::chrono::milliseconds(update_millisecond)); - -MotionEmu::MotionEmu(EmuWindow& emu_window) - : motion_emu_thread(&MotionEmu::MotionEmuThread, this, std::ref(emu_window)) {} - -MotionEmu::~MotionEmu() { - if (motion_emu_thread.joinable()) { - shutdown_event.Set(); - motion_emu_thread.join(); - } -} - -void MotionEmu::MotionEmuThread(EmuWindow& emu_window) { - auto update_time = std::chrono::steady_clock::now(); - Math::Quaternion q = MakeQuaternion(Math::Vec3(), 0); - Math::Quaternion old_q; - - while (!shutdown_event.WaitUntil(update_time)) { - update_time += update_duration; - old_q = q; - - { - std::lock_guard guard(tilt_mutex); - - // Find the quaternion describing current 3DS tilting - q = MakeQuaternion(Math::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), - tilt_angle); - } - - auto inv_q = q.Inverse(); - - // Set the gravity vector in world space - auto gravity = Math::MakeVec(0.0f, -1.0f, 0.0f); - - // Find the angular rate vector in world space - auto angular_rate = ((q - old_q) * inv_q).xyz * 2; - angular_rate *= 1000 / update_millisecond / MathUtil::PI * 180; - - // Transform the two vectors from world space to 3DS space - gravity = QuaternionRotate(inv_q, gravity); - angular_rate = QuaternionRotate(inv_q, angular_rate); - - // Update the sensor state - emu_window.AccelerometerChanged(gravity.x, gravity.y, gravity.z); - emu_window.GyroscopeChanged(angular_rate.x, angular_rate.y, angular_rate.z); - } -} - -void MotionEmu::BeginTilt(int x, int y) { - mouse_origin = Math::MakeVec(x, y); - is_tilting = true; -} - -void MotionEmu::Tilt(int x, int y) { - constexpr float SENSITIVITY = 0.01f; - auto mouse_move = Math::MakeVec(x, y) - mouse_origin; - if (is_tilting) { - std::lock_guard guard(tilt_mutex); - if (mouse_move.x == 0 && mouse_move.y == 0) { - tilt_angle = 0; - } else { - tilt_direction = mouse_move.Cast(); - tilt_angle = MathUtil::Clamp(tilt_direction.Normalize() * SENSITIVITY, 0.0f, - MathUtil::PI * 0.5f); - } - } -} - -void MotionEmu::EndTilt() { - std::lock_guard guard(tilt_mutex); - tilt_angle = 0; - is_tilting = false; -} - -} // namespace Motion diff --git a/src/core/frontend/motion_emu.h b/src/core/frontend/motion_emu.h deleted file mode 100644 index 99d41a726..000000000 --- a/src/core/frontend/motion_emu.h +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2016 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once -#include "common/thread.h" -#include "common/vector_math.h" - -class EmuWindow; - -namespace Motion { - -class MotionEmu final { -public: - MotionEmu(EmuWindow& emu_window); - ~MotionEmu(); - - /** - * Signals that a motion sensor tilt has begun. - * @param x the x-coordinate of the cursor - * @param y the y-coordinate of the cursor - */ - void BeginTilt(int x, int y); - - /** - * Signals that a motion sensor tilt is occurring. - * @param x the x-coordinate of the cursor - * @param y the y-coordinate of the cursor - */ - void Tilt(int x, int y); - - /** - * Signals that a motion sensor tilt has ended. - */ - void EndTilt(); - -private: - Math::Vec2 mouse_origin; - - std::mutex tilt_mutex; - Math::Vec2 tilt_direction; - float tilt_angle = 0; - - bool is_tilting = false; - - Common::Event shutdown_event; - std::thread motion_emu_thread; - - void MotionEmuThread(EmuWindow& emu_window); -}; - -} // namespace Motion -- cgit v1.2.3 From b67c2dc82cff794fc1989e103daa96f5ff5f12be Mon Sep 17 00:00:00 2001 From: MerryMage Date: Tue, 15 Aug 2017 10:16:50 +0100 Subject: dsp_dsp: Remove size assertion in LoadComponent --- src/core/hle/service/dsp_dsp.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/dsp_dsp.cpp b/src/core/hle/service/dsp_dsp.cpp index 7d746054f..42f8950f9 100644 --- a/src/core/hle/service/dsp_dsp.cpp +++ b/src/core/hle/service/dsp_dsp.cpp @@ -147,9 +147,10 @@ static void LoadComponent(Service::Interface* self) { LOG_INFO(Service_DSP, "Firmware hash: %#" PRIx64, Common::ComputeHash64(component_data.data(), component_data.size())); // Some versions of the firmware have the location of DSP structures listed here. - ASSERT(size > 0x37C); - LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, - Common::ComputeHash64(component_data.data() + 0x340, 60)); + if (size > 0x37C) { + LOG_INFO(Service_DSP, "Structures hash: %#" PRIx64, + Common::ComputeHash64(component_data.data() + 0x340, 60)); + } LOG_WARNING(Service_DSP, "(STUBBED) called size=0x%X, prog_mask=0x%08X, data_mask=0x%08X, buffer=0x%08X", -- cgit v1.2.3 From 5d0a1e7efddf234234d54fe97395f6975f8d1a28 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sat, 19 Aug 2017 19:14:33 +0200 Subject: Added missing parts in libnetwork (#2838) * Network: Set and send the game information over enet Added Callbacks for RoomMember and GetMemberList to Room in preparation for web_services. --- src/core/CMakeLists.txt | 2 +- src/core/core.cpp | 5 +++++ src/core/loader/ncch.cpp | 8 ++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..0a6f97e4b 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -388,7 +388,7 @@ set(HEADERS create_directory_groups(${SRCS} ${HEADERS}) add_library(core STATIC ${SRCS} ${HEADERS}) -target_link_libraries(core PUBLIC common PRIVATE audio_core video_core) +target_link_libraries(core PUBLIC common PRIVATE audio_core network video_core) target_link_libraries(core PUBLIC Boost::boost PRIVATE cryptopp dynarmic fmt) if (ENABLE_WEB_SERVICE) target_link_libraries(core PUBLIC json-headers web_service) diff --git a/src/core/core.cpp b/src/core/core.cpp index d08f18623..5332318cf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -19,6 +19,7 @@ #include "core/loader/loader.h" #include "core/memory_setup.h" #include "core/settings.h" +#include "network/network.h" #include "video_core/video_core.h" namespace Core { @@ -188,6 +189,10 @@ void System::Shutdown() { cpu_core = nullptr; app_loader = nullptr; telemetry_session = nullptr; + if (auto room_member = Network::GetRoomMember().lock()) { + Network::GameInfo game_info{}; + room_member->SendGameInfo(game_info); + } LOG_DEBUG(Core, "Shutdown OK"); } diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c007069a9..7aff7f29b 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -20,6 +20,7 @@ #include "core/loader/ncch.h" #include "core/loader/smdh.h" #include "core/memory.h" +#include "network/network.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -350,6 +351,13 @@ ResultStatus AppLoader_NCCH::Load() { Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); + if (auto room_member = Network::GetRoomMember().lock()) { + Network::GameInfo game_info; + ReadTitle(game_info.name); + game_info.id = ncch_header.program_id; + room_member->SendGameInfo(game_info); + } + is_loaded = true; // Set state to loaded result = LoadExec(); // Load the executable into memory for booting -- cgit v1.2.3 From 54c0c8adee90d374e954e742d6d03279ef2e7ed7 Mon Sep 17 00:00:00 2001 From: wwylele Date: Sun, 20 Aug 2017 08:37:48 +0300 Subject: HID: fix a comment and a warning --- src/core/hle/service/hid/hid.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index a13b72e88..31f34a7ae 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -203,7 +203,7 @@ static void UpdateAccelerometerCallback(u64 userdata, int cycles_late) { Math::Vec3 accel; std::tie(accel, std::ignore) = motion_device->GetStatus(); accel *= accelerometer_coef; - // TODO(wwylele): do a time stretch as it in UpdateGyroscopeCallback + // TODO(wwylele): do a time stretch like the one in UpdateGyroscopeCallback // The time stretch formula should be like // stretched_vector = (raw_vector - gravity) * stretch_ratio + gravity @@ -246,7 +246,7 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { Math::Vec3 gyro; std::tie(std::ignore, gyro) = motion_device->GetStatus(); - float stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); + double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); gyro *= gyroscope_coef * stretch; gyroscope_entry.x = static_cast(gyro.x); gyroscope_entry.y = static_cast(gyro.y); -- cgit v1.2.3 From d3fb1d6c3882e45dee66fe7e5cf7613268e1b7bb Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 19 Aug 2017 11:28:22 -0500 Subject: Dyncom: Fixed a conversion warning when decoding thumb instructions. --- src/core/arm/dyncom/arm_dyncom_interpreter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index f4fbb8d04..f829b9229 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -759,7 +759,7 @@ static ThumbDecodeStatus DecodeThumbInstruction(u32 inst, u32 addr, u32* arm_ins ThumbDecodeStatus ret = TranslateThumbInstruction(addr, inst, arm_inst, inst_size); if (ret == ThumbDecodeStatus::BRANCH) { int inst_index; - int table_length = arm_instruction_trans_len; + int table_length = static_cast(arm_instruction_trans_len); u32 tinstr = GetThumbInstruction(inst, addr); switch ((tinstr & 0xF800) >> 11) { -- cgit v1.2.3 From 9d0841b48b6b6f3c5a2425922617343fc2f79cdc Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 19 Aug 2017 11:30:20 -0500 Subject: Dyncom: Use size_t instead of int to store the instruction offsets in the instruction cache. Fixes a few warnings. --- src/core/arm/dyncom/arm_dyncom_interpreter.cpp | 6 +++--- src/core/arm/skyeye_common/armstate.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index f829b9229..3522d1e82 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -838,7 +838,7 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons return inst_size; } -static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) { +static int InterpreterTranslateBlock(ARMul_State* cpu, std::size_t& bb_start, u32 addr) { MICROPROFILE_SCOPE(DynCom_Decode); // Decode instruction, get index @@ -871,7 +871,7 @@ static int InterpreterTranslateBlock(ARMul_State* cpu, int& bb_start, u32 addr) return KEEP_GOING; } -static int InterpreterTranslateSingle(ARMul_State* cpu, int& bb_start, u32 addr) { +static int InterpreterTranslateSingle(ARMul_State* cpu, std::size_t& bb_start, u32 addr) { MICROPROFILE_SCOPE(DynCom_Decode); ARM_INST_PTR inst_base = nullptr; @@ -1620,7 +1620,7 @@ unsigned InterpreterMainLoop(ARMul_State* cpu) { unsigned int addr; unsigned int num_instrs = 0; - int ptr; + std::size_t ptr; LOAD_NZCVT; DISPATCH : { diff --git a/src/core/arm/skyeye_common/armstate.h b/src/core/arm/skyeye_common/armstate.h index 1a707ff7e..893877797 100644 --- a/src/core/arm/skyeye_common/armstate.h +++ b/src/core/arm/skyeye_common/armstate.h @@ -230,7 +230,7 @@ public: // TODO(bunnei): Move this cache to a better place - it should be per codeset (likely per // process for our purposes), not per ARMul_State (which tracks CPU core state). - std::unordered_map instruction_cache; + std::unordered_map instruction_cache; private: void ResetMPCoreCP15Registers(); -- cgit v1.2.3 From d237a890482b62c90c58691863dabd609c2aa34e Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 19 Aug 2017 11:33:01 -0500 Subject: CPU/Dynarmic: Fixed a warning when incrementing the number of ticks in ExecuteInstructions. --- src/core/arm/dynarmic/arm_dynarmic.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 7d2790b08..0a0b91590 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -136,7 +136,7 @@ MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { MICROPROFILE_SCOPE(ARM_Jit); - unsigned ticks_executed = jit->Run(static_cast(num_instructions)); + std::size_t ticks_executed = jit->Run(static_cast(num_instructions)); AddTicks(ticks_executed); } -- cgit v1.2.3 From 145a7293a32c6fb25f1db796f7e1dd6c4efc985e Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 19 Aug 2017 11:37:21 -0500 Subject: HLE/Applets: Fixed some conversion warnings when creating the framebuffer shared memory objects. --- src/core/hle/applets/erreula.cpp | 4 ++-- src/core/hle/applets/mii_selector.cpp | 4 ++-- src/core/hle/applets/mint.cpp | 4 ++-- src/core/hle/applets/swkbd.cpp | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/applets/erreula.cpp b/src/core/hle/applets/erreula.cpp index 75d7fd9fc..518f371f5 100644 --- a/src/core/hle/applets/erreula.cpp +++ b/src/core/hle/applets/erreula.cpp @@ -31,8 +31,8 @@ ResultCode ErrEula::ReceiveParameter(const Service::APT::MessageParameter& param heap_memory = std::make_shared>(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, "ErrEula Memory"); + heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + "ErrEula Memory"); // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 89f08daa2..705859f1e 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -38,8 +38,8 @@ ResultCode MiiSelector::ReceiveParameter(const Service::APT::MessageParameter& p heap_memory = std::make_shared>(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, "MiiSelector Memory"); + heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + "MiiSelector Memory"); // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; diff --git a/src/core/hle/applets/mint.cpp b/src/core/hle/applets/mint.cpp index 31a79ea17..50d79190b 100644 --- a/src/core/hle/applets/mint.cpp +++ b/src/core/hle/applets/mint.cpp @@ -31,8 +31,8 @@ ResultCode Mint::ReceiveParameter(const Service::APT::MessageParameter& paramete heap_memory = std::make_shared>(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"); + heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + "Mint Memory"); // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; diff --git a/src/core/hle/applets/swkbd.cpp b/src/core/hle/applets/swkbd.cpp index fdf8807b0..0bc471a3a 100644 --- a/src/core/hle/applets/swkbd.cpp +++ b/src/core/hle/applets/swkbd.cpp @@ -41,8 +41,8 @@ ResultCode SoftwareKeyboard::ReceiveParameter(Service::APT::MessageParameter con heap_memory = std::make_shared>(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, "SoftwareKeyboard Memory"); + heap_memory, 0, capture_info.size, MemoryPermission::ReadWrite, MemoryPermission::ReadWrite, + "SoftwareKeyboard Memory"); // Send the response message with the newly created SharedMemory Service::APT::MessageParameter result; -- cgit v1.2.3 From 65f19b51c43fbc35a1f1bfb81d773eaf6b5fffd3 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 19 Aug 2017 12:04:40 -0500 Subject: Warnings: Add UNREACHABLE macros to switches that contemplate all possible values. --- src/core/file_sys/archive_backend.cpp | 2 ++ src/core/hle/kernel/kernel.h | 3 +++ src/core/hw/gpu.h | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/archive_backend.cpp b/src/core/file_sys/archive_backend.cpp index 1fae0ede0..87a240d7a 100644 --- a/src/core/file_sys/archive_backend.cpp +++ b/src/core/file_sys/archive_backend.cpp @@ -90,6 +90,8 @@ std::u16string Path::AsU16Str() const { LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); return {}; } + + UNREACHABLE(); } std::vector Path::AsBinary() const { diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 9cf288b08..142bb84b2 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -8,6 +8,7 @@ #include #include #include +#include "common/assert.h" #include "common/common_types.h" namespace Kernel { @@ -84,6 +85,8 @@ public: case HandleType::ClientSession: return false; } + + UNREACHABLE(); } public: diff --git a/src/core/hw/gpu.h b/src/core/hw/gpu.h index 21b127fee..e3d0a0e08 100644 --- a/src/core/hw/gpu.h +++ b/src/core/hw/gpu.h @@ -74,9 +74,9 @@ struct Regs { case PixelFormat::RGB5A1: case PixelFormat::RGBA4: return 2; - default: - UNIMPLEMENTED(); } + + UNREACHABLE(); } INSERT_PADDING_WORDS(0x4); -- cgit v1.2.3 From 8a9a4e2c42b6c2326140f7369374bf78a4645a8f Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 19 Aug 2017 12:09:35 -0500 Subject: GPU/Warnings: Explicitly cast the screen refresh ticks to u64. --- src/core/hw/gpu.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/hw/gpu.cpp b/src/core/hw/gpu.cpp index 6838e449c..83ad9d898 100644 --- a/src/core/hw/gpu.cpp +++ b/src/core/hw/gpu.cpp @@ -29,7 +29,7 @@ namespace GPU { Regs g_regs; /// 268MHz CPU clocks / 60Hz frames per second -const u64 frame_ticks = BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE; +const u64 frame_ticks = static_cast(BASE_CLOCK_RATE_ARM11 / SCREEN_REFRESH_RATE); /// Event id for CoreTiming static int vblank_event; -- cgit v1.2.3 From fa228ca637b84e6441879769d54a531ab6aba113 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 21 Aug 2017 20:54:29 -0500 Subject: Kernel/Threads: Don't immediately switch to the new main thread when loading a new process. This is necessary for loading multiple processes at the same time. The main thread will be automatically scheduled when necessary once the scheduler runs. --- src/core/hle/kernel/thread.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f5f2eb2f7..b957c45dd 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -478,8 +478,6 @@ void Thread::BoostPriority(s32 priority) { } SharedPtr SetupMainThread(u32 entry_point, s32 priority) { - DEBUG_ASSERT(!GetCurrentThread()); - // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, Memory::HEAP_VADDR_END); @@ -489,9 +487,7 @@ SharedPtr SetupMainThread(u32 entry_point, s32 priority) { thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010 - // Run new "main" thread - SwitchContext(thread.get()); - + // Note: The newly created thread will be run when the scheduler fires. return thread; } -- cgit v1.2.3 From bca8916cea9c437d82509f8350fa3b858720f90e Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 20 Jul 2017 23:52:50 -0500 Subject: Kernel/HLE: Use a mutex to synchronize access to the HLE kernel state between the cpu thread and any other possible threads that might touch the kernel (network thread, etc). This mutex is acquired in SVC::CallSVC, ie, as soon as the guest application enters the HLE kernel, and should be acquired by the aforementioned threads before modifying kernel structures. --- src/core/CMakeLists.txt | 2 ++ src/core/hle/kernel/kernel.h | 2 +- src/core/hle/lock.cpp | 11 +++++++++++ src/core/hle/lock.h | 18 ++++++++++++++++++ src/core/hle/svc.cpp | 8 ++++++-- 5 files changed, 38 insertions(+), 3 deletions(-) create mode 100644 src/core/hle/lock.cpp create mode 100644 src/core/hle/lock.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 360f407f3..14027e182 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRCS hle/kernel/timer.cpp hle/kernel/vm_manager.cpp hle/kernel/wait_object.cpp + hle/lock.cpp hle/romfs.cpp hle/service/ac/ac.cpp hle/service/ac/ac_i.cpp @@ -258,6 +259,7 @@ set(HEADERS hle/kernel/timer.h hle/kernel/vm_manager.h hle/kernel/wait_object.h + hle/lock.h hle/result.h hle/romfs.h hle/service/ac/ac.h diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h index 9cf288b08..255cda359 100644 --- a/src/core/hle/kernel/kernel.h +++ b/src/core/hle/kernel/kernel.h @@ -129,4 +129,4 @@ void Init(u32 system_mode); /// Shutdown the kernel void Shutdown(); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp new file mode 100644 index 000000000..082f689c8 --- /dev/null +++ b/src/core/hle/lock.cpp @@ -0,0 +1,11 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace HLE { +std::mutex g_hle_lock; +} diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h new file mode 100644 index 000000000..8265621e1 --- /dev/null +++ b/src/core/hle/lock.h @@ -0,0 +1,18 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include + +namespace HLE { +/* + * Synchronizes access to the internal HLE kernel structures, it is acquired when a guest + * application thread performs a syscall. It should be acquired by any host threads that read or + * modify the HLE kernel state. Note: Any operation that directly or indirectly reads from or writes + * to the emulated memory is not protected by this mutex, and should be avoided in any threads other + * than the CPU thread. + */ +extern std::mutex g_hle_lock; +} // namespace HLE diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index e4b803046..b98938cb4 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -31,6 +31,7 @@ #include "core/hle/kernel/timer.h" #include "core/hle/kernel/vm_manager.h" #include "core/hle/kernel/wait_object.h" +#include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/service.h" @@ -1188,7 +1189,7 @@ struct FunctionDef { Func* func; const char* name; }; -} +} // namespace static const FunctionDef SVC_Table[] = { {0x00, nullptr, "Unknown"}, @@ -1332,6 +1333,9 @@ MICROPROFILE_DEFINE(Kernel_SVC, "Kernel", "SVC", MP_RGB(70, 200, 70)); void CallSVC(u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); + // Lock the global kernel mutex when we enter the kernel HLE. + std::lock_guard lock(HLE::g_hle_lock); + const FunctionDef* info = GetSVCInfo(immediate); if (info) { if (info->func) { @@ -1342,4 +1346,4 @@ void CallSVC(u32 immediate) { } } -} // namespace +} // namespace SVC -- cgit v1.2.3 From f484927ed03a1943a83f8781e598e07c056cc82a Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 00:34:47 -0500 Subject: Kernel/Memory: Acquire the global HLE lock when a memory read/write operation falls outside of the fast path, for it might perform an MMIO operation. --- src/core/memory.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 9024f4922..72cbf2ec7 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "common/swap.h" #include "core/hle/kernel/process.h" +#include "core/hle/lock.h" #include "core/memory.h" #include "core/memory_setup.h" #include "core/mmio.h" @@ -187,6 +188,9 @@ T Read(const VAddr vaddr) { return value; } + // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state + std::lock_guard lock(HLE::g_hle_lock); + PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { case PageType::Unmapped: @@ -226,6 +230,9 @@ void Write(const VAddr vaddr, const T data) { return; } + // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state + std::lock_guard lock(HLE::g_hle_lock); + PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { case PageType::Unmapped: @@ -722,4 +729,4 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { return addr | 0x80000000; } -} // namespace +} // namespace Memory -- cgit v1.2.3 From c84e60b4700778db236d3177714a076c515f9ce7 Mon Sep 17 00:00:00 2001 From: wwylele Date: Tue, 8 Aug 2017 21:34:17 +0300 Subject: HID: use TouchDevice for touch pad --- src/core/frontend/input.h | 6 ++++++ src/core/hle/service/hid/hid.cpp | 12 ++++++++---- src/core/settings.h | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h index 5916a901d..8c256beb5 100644 --- a/src/core/frontend/input.h +++ b/src/core/frontend/input.h @@ -126,4 +126,10 @@ using AnalogDevice = InputDevice>; */ using MotionDevice = InputDevice, Math::Vec3>>; +/** + * A touch device is an input device that returns a tuple of two floats and a bool. The floats are + * x and y coordinates in the range 0.0 - 1.0, and the bool indicates whether it is pressed. + */ +using TouchDevice = InputDevice>; + } // namespace Input diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 31f34a7ae..aa5d821f9 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -7,9 +7,9 @@ #include #include #include "common/logging/log.h" +#include "core/3ds.h" #include "core/core.h" #include "core/core_timing.h" -#include "core/frontend/emu_window.h" #include "core/frontend/input.h" #include "core/hle/ipc.h" #include "core/hle/kernel/event.h" @@ -19,7 +19,6 @@ #include "core/hle/service/hid/hid_spvr.h" #include "core/hle/service/hid/hid_user.h" #include "core/hle/service/service.h" -#include "video_core/video_core.h" namespace Service { namespace HID { @@ -59,6 +58,7 @@ static std::array, Settings::NativeButton:: buttons; static std::unique_ptr circle_pad; static std::unique_ptr motion_device; +static std::unique_ptr touch_device; DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions @@ -96,6 +96,7 @@ static void LoadInputDevices() { circle_pad = Input::CreateDevice( Settings::values.analogs[Settings::NativeAnalog::CirclePad]); motion_device = Input::CreateDevice(Settings::values.motion_device); + touch_device = Input::CreateDevice(Settings::values.touch_device); } static void UnloadInputDevices() { @@ -104,6 +105,7 @@ static void UnloadInputDevices() { } circle_pad.reset(); motion_device.reset(); + touch_device.reset(); } static void UpdatePadCallback(u64 userdata, int cycles_late) { @@ -172,8 +174,10 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) { // Get the current touch entry TouchDataEntry& touch_entry = mem->touch.entries[mem->touch.index]; bool pressed = false; - - std::tie(touch_entry.x, touch_entry.y, pressed) = VideoCore::g_emu_window->GetTouchState(); + float x, y; + std::tie(x, y, pressed) = touch_device->GetStatus(); + touch_entry.x = static_cast(x * Core::kScreenBottomWidth); + touch_entry.y = static_cast(y * Core::kScreenBottomHeight); touch_entry.valid.Assign(pressed ? 1 : 0); // TODO(bunnei): We're not doing anything with offset 0xA8 + 0x18 of HID SharedMemory, which diff --git a/src/core/settings.h b/src/core/settings.h index 7e15b119b..088d7651a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -80,6 +80,7 @@ struct Values { std::array buttons; std::array analogs; std::string motion_device; + std::string touch_device; // Core bool use_cpu_jit; -- cgit v1.2.3 From 2617de1fe6f6f1fc846a8e038e1ea77a894554b2 Mon Sep 17 00:00:00 2001 From: wwylele Date: Wed, 9 Aug 2017 02:57:42 +0300 Subject: EmuWindow: refactor touch input into a TouchDevice --- src/core/frontend/emu_window.cpp | 71 ++++++++++++++++++++++++++++++++-------- src/core/frontend/emu_window.h | 31 +++--------------- 2 files changed, 63 insertions(+), 39 deletions(-) (limited to 'src/core') diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 60b20d4e2..787c517ff 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -2,14 +2,55 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#include #include -#include "common/assert.h" -#include "core/3ds.h" -#include "core/core.h" +#include #include "core/frontend/emu_window.h" +#include "core/frontend/input.h" #include "core/settings.h" +class EmuWindow::TouchState : public Input::Factory, + public std::enable_shared_from_this { +public: + std::unique_ptr Create(const Common::ParamPackage&) override { + return std::make_unique(shared_from_this()); + } + + std::mutex mutex; + + bool touch_pressed = false; ///< True if touchpad area is currently pressed, otherwise false + + float touch_x = 0.0f; ///< Touchpad X-position + float touch_y = 0.0f; ///< Touchpad Y-position + +private: + class Device : public Input::TouchDevice { + public: + explicit Device(std::weak_ptr&& touch_state) : touch_state(touch_state) {} + std::tuple GetStatus() const override { + if (auto state = touch_state.lock()) { + std::lock_guard guard(state->mutex); + return std::make_tuple(state->touch_x, state->touch_y, state->touch_pressed); + } + return std::make_tuple(0.0f, 0.0f, false); + } + + private: + std::weak_ptr touch_state; + }; +}; + +EmuWindow::EmuWindow() { + // TODO: Find a better place to set this. + config.min_client_area_size = std::make_pair(400u, 480u); + active_config = config; + touch_state = std::make_shared(); + Input::RegisterFactory("emu_window", touch_state); +} + +EmuWindow::~EmuWindow() { + Input::UnregisterFactory("emu_window"); +} + /** * Check if the given x/y coordinates are within the touchpad specified by the framebuffer layout * @param layout FramebufferLayout object describing the framebuffer size and screen positions @@ -38,22 +79,26 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) { if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) return; - touch_x = Core::kScreenBottomWidth * (framebuffer_x - framebuffer_layout.bottom_screen.left) / - (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); - touch_y = Core::kScreenBottomHeight * (framebuffer_y - framebuffer_layout.bottom_screen.top) / - (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); + std::lock_guard guard(touch_state->mutex); + touch_state->touch_x = + static_cast(framebuffer_x - framebuffer_layout.bottom_screen.left) / + (framebuffer_layout.bottom_screen.right - framebuffer_layout.bottom_screen.left); + touch_state->touch_y = + static_cast(framebuffer_y - framebuffer_layout.bottom_screen.top) / + (framebuffer_layout.bottom_screen.bottom - framebuffer_layout.bottom_screen.top); - touch_pressed = true; + touch_state->touch_pressed = true; } void EmuWindow::TouchReleased() { - touch_pressed = false; - touch_x = 0; - touch_y = 0; + std::lock_guard guard(touch_state->mutex); + touch_state->touch_pressed = false; + touch_state->touch_x = 0; + touch_state->touch_y = 0; } void EmuWindow::TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y) { - if (!touch_pressed) + if (!touch_state->touch_pressed) return; if (!IsWithinTouchscreen(framebuffer_layout, framebuffer_x, framebuffer_y)) diff --git a/src/core/frontend/emu_window.h b/src/core/frontend/emu_window.h index 7bdee251c..c10dee51b 100644 --- a/src/core/frontend/emu_window.h +++ b/src/core/frontend/emu_window.h @@ -4,11 +4,10 @@ #pragma once -#include +#include #include #include #include "common/common_types.h" -#include "common/math_util.h" #include "core/frontend/framebuffer_layout.h" /** @@ -68,17 +67,6 @@ public: */ void TouchMoved(unsigned framebuffer_x, unsigned framebuffer_y); - /** - * Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed). - * @note This should be called by the core emu thread to get a state set by the window thread. - * @todo Fix this function to be thread-safe. - * @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and - * `pressed` is true if the touch screen is currently being pressed - */ - std::tuple GetTouchState() const { - return std::make_tuple(touch_x, touch_y, touch_pressed); - } - /** * Returns currently active configuration. * @note Accesses to the returned object need not be consistent because it may be modified in @@ -113,15 +101,8 @@ public: void UpdateCurrentFramebufferLayout(unsigned width, unsigned height); protected: - EmuWindow() { - // TODO: Find a better place to set this. - config.min_client_area_size = std::make_pair(400u, 480u); - active_config = config; - touch_x = 0; - touch_y = 0; - touch_pressed = false; - } - virtual ~EmuWindow() {} + EmuWindow(); + virtual ~EmuWindow(); /** * Processes any pending configuration changes from the last SetConfig call. @@ -177,10 +158,8 @@ private: /// ProcessConfigurationChanges) WindowConfig active_config; ///< Internal active configuration - bool touch_pressed; ///< True if touchpad area is currently pressed, otherwise false - - u16 touch_x; ///< Touchpad X-position in native 3DS pixel coordinates (0-320) - u16 touch_y; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240) + class TouchState; + std::shared_ptr touch_state; /** * Clip the provided coordinates to be inside the touchscreen area. -- cgit v1.2.3 From 3cdf854e44e7ff088fa0cbdcfa2bcc6e41822b2c Mon Sep 17 00:00:00 2001 From: ThaMighty90 <30285364+ThaMighty90@users.noreply.github.com> Date: Fri, 25 Aug 2017 23:53:07 +0200 Subject: SidebySide Layout (#2859) * added a SidebySide Layout * Reworked, so both screen have the same height and cleaned up screen translates. * added the option in the UI, hope this is the right way to do it. formated framebuffer_layout.cpp * delete the x64 files * deleted ui_configure_graphics.h * added Option for the Layout in the xml * got rid of SIDE_BY_SIDE_ASPECT_RATIO because it was useless. pulled translate into variables * changed shift variables to u32 and moved them in their respective branch. remove notr="true" for the Screen layout drop down * reworked intends :). changed function description for SideFrameLayout * some description reworking --- src/core/frontend/emu_window.cpp | 3 +++ src/core/frontend/framebuffer_layout.cpp | 36 +++++++++++++++++++++++++++++++- src/core/frontend/framebuffer_layout.h | 11 ++++++++++ src/core/settings.cpp | 2 +- src/core/settings.h | 5 +++-- 5 files changed, 53 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp index 60b20d4e2..54fa5c7fa 100644 --- a/src/core/frontend/emu_window.cpp +++ b/src/core/frontend/emu_window.cpp @@ -74,6 +74,9 @@ void EmuWindow::UpdateCurrentFramebufferLayout(unsigned width, unsigned height) case Settings::LayoutOption::LargeScreen: layout = Layout::LargeFrameLayout(width, height, Settings::values.swap_screen); break; + case Settings::LayoutOption::SideScreen: + layout = Layout::SideFrameLayout(width, height, Settings::values.swap_screen); + break; case Settings::LayoutOption::Default: default: layout = Layout::DefaultFrameLayout(width, height, Settings::values.swap_screen); diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp index d2d02f9ff..e9f778fcb 100644 --- a/src/core/frontend/framebuffer_layout.cpp +++ b/src/core/frontend/framebuffer_layout.cpp @@ -141,6 +141,40 @@ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool swapped return res; } +FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool swapped) { + ASSERT(width > 0); + ASSERT(height > 0); + + FramebufferLayout res{width, height, true, true, {}, {}}; + // Aspect ratio of both screens side by side + const float emulation_aspect_ratio = static_cast(Core::kScreenTopHeight) / + (Core::kScreenTopWidth + Core::kScreenBottomWidth); + float window_aspect_ratio = static_cast(height) / width; + MathUtil::Rectangle screen_window_area{0, 0, width, height}; + // Find largest Rectangle that can fit in the window size with the given aspect ratio + MathUtil::Rectangle screen_rect = + maxRectangle(screen_window_area, emulation_aspect_ratio); + // Find sizes of top and bottom screen + MathUtil::Rectangle top_screen = maxRectangle(screen_rect, TOP_SCREEN_ASPECT_RATIO); + MathUtil::Rectangle bot_screen = maxRectangle(screen_rect, BOT_SCREEN_ASPECT_RATIO); + + if (window_aspect_ratio < emulation_aspect_ratio) { + // Apply borders to the left and right sides of the window. + u32 shift_horizontal = (screen_window_area.GetWidth() - screen_rect.GetWidth()) / 2; + top_screen = top_screen.TranslateX(shift_horizontal); + bot_screen = bot_screen.TranslateX(shift_horizontal); + } else { + // Window is narrower than the emulation content => apply borders to the top and bottom + u32 shift_vertical = (screen_window_area.GetHeight() - screen_rect.GetHeight()) / 2; + top_screen = top_screen.TranslateY(shift_vertical); + bot_screen = bot_screen.TranslateY(shift_vertical); + } + // Move the top screen to the right if we are swapped. + res.top_screen = swapped ? top_screen.TranslateX(bot_screen.GetWidth()) : top_screen; + res.bottom_screen = swapped ? bot_screen : bot_screen.TranslateX(top_screen.GetWidth()); + return res; +} + FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { ASSERT(width > 0); ASSERT(height > 0); @@ -158,4 +192,4 @@ FramebufferLayout CustomFrameLayout(unsigned width, unsigned height) { res.bottom_screen = bot_screen; return res; } -} +} // namespace Layout diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h index 9a7738969..4983cf103 100644 --- a/src/core/frontend/framebuffer_layout.h +++ b/src/core/frontend/framebuffer_layout.h @@ -53,6 +53,17 @@ FramebufferLayout SingleFrameLayout(unsigned width, unsigned height, bool is_swa */ FramebufferLayout LargeFrameLayout(unsigned width, unsigned height, bool is_swapped); +/** +* Factory method for constructing a Frame with the Top screen and bottom +* screen side by side +* This is useful for devices with small screens, like the GPDWin +* @param width Window framebuffer width in pixels +* @param height Window framebuffer height in pixels +* @param is_swapped if true, the bottom screen will be the left display +* @return Newly created FramebufferLayout object with default screen regions initialized +*/ +FramebufferLayout SideFrameLayout(unsigned width, unsigned height, bool is_swapped); + /** * Factory method for constructing a custom FramebufferLayout * @param width Window framebuffer width in pixels diff --git a/src/core/settings.cpp b/src/core/settings.cpp index d4f0429d1..efcf1267d 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -36,4 +36,4 @@ void Apply() { Service::IR::ReloadInputDevices(); } -} // namespace +} // namespace Settings diff --git a/src/core/settings.h b/src/core/settings.h index 7e15b119b..ca657719a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -15,6 +15,7 @@ enum class LayoutOption { Default, SingleScreen, LargeScreen, + SideScreen, }; namespace NativeButton { @@ -70,7 +71,7 @@ enum Values { static const std::array mapping = {{ "circle_pad", "c_stick", }}; -} // namespace NumAnalog +} // namespace NativeAnalog struct Values { // CheckNew3DS @@ -137,4 +138,4 @@ struct Values { static constexpr int REGION_VALUE_AUTO_SELECT = -1; void Apply(); -} +} // namespace Settings -- cgit v1.2.3 From d6a819c7cb1ac00671b7e76b23d7d065d1fd76a3 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 22 Aug 2017 20:58:19 -0400 Subject: telemetry_session: Log telemetry ID. --- src/core/telemetry_session.cpp | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'src/core') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 94483f385..61ba78457 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -3,8 +3,10 @@ // Refer to the license.txt file included. #include +#include #include "common/assert.h" +#include "common/file_util.h" #include "common/scm_rev.h" #include "common/x64/cpu_detect.h" #include "core/core.h" @@ -29,12 +31,46 @@ static const char* CpuVendorToStr(Common::CPUVendor vendor) { UNREACHABLE(); } +static u64 GenerateTelemetryId() { + u64 telemetry_id{}; + CryptoPP::AutoSeededRandomPool rng; + rng.GenerateBlock(reinterpret_cast(&telemetry_id), sizeof(u64)); + return telemetry_id; +} + +static u64 GetTelemetryId() { + u64 telemetry_id{}; + static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"}; + + if (FileUtil::Exists(filename)) { + FileUtil::IOFile file(filename, "rb"); + if (!file.IsOpen()) { + LOG_ERROR(WebService, "failed to open telemetry_id: %s", filename.c_str()); + return {}; + } + file.ReadBytes(&telemetry_id, sizeof(u64)); + } else { + FileUtil::IOFile file(filename, "wb"); + if (!file.IsOpen()) { + LOG_ERROR(WebService, "failed to open telemetry_id: %s", filename.c_str()); + return {}; + } + telemetry_id = GenerateTelemetryId(); + file.WriteBytes(&telemetry_id, sizeof(u64)); + } + + return telemetry_id; +} + TelemetrySession::TelemetrySession() { #ifdef ENABLE_WEB_SERVICE backend = std::make_unique(); #else backend = std::make_unique(); #endif + // Log one-time top-level information + AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId()); + // Log one-time session start information const s64 init_time{std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch()) -- cgit v1.2.3 From c781aea947e275a970a3431a36160b769865993d Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 22 Aug 2017 22:37:03 -0400 Subject: settings: Add enable_telemetry, citra_username, and citra_token. --- src/core/settings.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/core') diff --git a/src/core/settings.h b/src/core/settings.h index ca657719a..bf8014c5a 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -130,7 +130,10 @@ struct Values { u16 gdbstub_port; // WebService + bool enable_telemetry; std::string telemetry_endpoint_url; + std::string citra_username; + std::string citra_token; } extern values; // a special value for Values::region_value indicating that citra will automatically select a region -- cgit v1.2.3 From 9f0da33c3349df47580d93fcd25346be4d2b94a7 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 23 Aug 2017 00:08:07 -0400 Subject: qt: Add an option to view/regenerate telemetry ID. --- src/core/telemetry_session.cpp | 19 ++++++++++++++++--- src/core/telemetry_session.h | 12 ++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 61ba78457..d0f257f58 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -38,21 +38,21 @@ static u64 GenerateTelemetryId() { return telemetry_id; } -static u64 GetTelemetryId() { +u64 GetTelemetryId() { u64 telemetry_id{}; static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"}; if (FileUtil::Exists(filename)) { FileUtil::IOFile file(filename, "rb"); if (!file.IsOpen()) { - LOG_ERROR(WebService, "failed to open telemetry_id: %s", filename.c_str()); + LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str()); return {}; } file.ReadBytes(&telemetry_id, sizeof(u64)); } else { FileUtil::IOFile file(filename, "wb"); if (!file.IsOpen()) { - LOG_ERROR(WebService, "failed to open telemetry_id: %s", filename.c_str()); + LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str()); return {}; } telemetry_id = GenerateTelemetryId(); @@ -62,6 +62,19 @@ static u64 GetTelemetryId() { return telemetry_id; } +u64 RegenerateTelemetryId() { + const u64 new_telemetry_id{GenerateTelemetryId()}; + static const std::string& filename{FileUtil::GetUserPath(D_CONFIG_IDX) + "telemetry_id"}; + + FileUtil::IOFile file(filename, "wb"); + if (!file.IsOpen()) { + LOG_ERROR(Core, "failed to open telemetry_id: %s", filename.c_str()); + return {}; + } + file.WriteBytes(&new_telemetry_id, sizeof(u64)); + return new_telemetry_id; +} + TelemetrySession::TelemetrySession() { #ifdef ENABLE_WEB_SERVICE backend = std::make_unique(); diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index cf53835c3..65613daae 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -35,4 +35,16 @@ private: std::unique_ptr backend; ///< Backend interface that logs fields }; +/** + * Gets TelemetryId, a unique identifier used for the user's telemetry sessions. + * @returns The current TelemetryId for the session. + */ +u64 GetTelemetryId(); + +/** + * Regenerates TelemetryId, a unique identifier used for the user's telemetry sessions. + * @returns The new TelemetryId that was generated. + */ +u64 RegenerateTelemetryId(); + } // namespace Core -- cgit v1.2.3 From 04bd0c957e583a518121626deb029f214cc98cf6 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 23 Aug 2017 21:09:34 -0400 Subject: web_services: Refactor to remove dependency on Core. --- src/core/telemetry_session.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index d0f257f58..104a16cc9 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -77,7 +77,13 @@ u64 RegenerateTelemetryId() { TelemetrySession::TelemetrySession() { #ifdef ENABLE_WEB_SERVICE - backend = std::make_unique(); + if (Settings::values.enable_telemetry) { + backend = std::make_unique( + Settings::values.telemetry_endpoint_url, Settings::values.citra_username, + Settings::values.citra_token); + } else { + backend = std::make_unique(); + } #else backend = std::make_unique(); #endif -- cgit v1.2.3 From 54411bef4eb16af0822820205a923690ea7e822a Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 17 Jul 2017 09:25:58 -0500 Subject: Services/UDS: Add functions to generate 802.11 auth and assoc response frames. --- src/core/CMakeLists.txt | 2 + src/core/hle/service/nwm/nwm_uds.h | 12 +++++ src/core/hle/service/nwm/uds_beacon.h | 11 ---- src/core/hle/service/nwm/uds_connection.cpp | 79 +++++++++++++++++++++++++++++ src/core/hle/service/nwm/uds_connection.h | 51 +++++++++++++++++++ 5 files changed, 144 insertions(+), 11 deletions(-) create mode 100644 src/core/hle/service/nwm/uds_connection.cpp create mode 100644 src/core/hle/service/nwm/uds_connection.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index ea09819e5..0719138af 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_connection.cpp hle/service/nwm/uds_data.cpp hle/service/pm_app.cpp hle/service/ptm/ptm.cpp @@ -342,6 +343,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_connection.h hle/service/nwm/uds_data.h hle/service/pm_app.h hle/service/ptm/ptm.h diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 141f49f9c..f1caaf974 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -42,6 +42,7 @@ using NodeList = std::vector; enum class NetworkStatus { NotConnected = 3, ConnectedAsHost = 6, + Connecting = 7, ConnectedAsClient = 9, ConnectedAsSpectator = 10, }; @@ -85,6 +86,17 @@ static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wron static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); +/// Additional block tag ids in the Beacon and Association Response frames +enum class TagId : u8 { + SSID = 0, + SupportedRates = 1, + DSParameterSet = 2, + TrafficIndicationMap = 5, + CountryInformation = 7, + ERPInformation = 42, + VendorSpecific = 221 +}; + class NWM_UDS final : public Interface { public: NWM_UDS(); diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h index caacf4c6f..c726b04d9 100644 --- a/src/core/hle/service/nwm/uds_beacon.h +++ b/src/core/hle/service/nwm/uds_beacon.h @@ -17,17 +17,6 @@ namespace NWM { using MacAddress = std::array; constexpr std::array NintendoOUI = {0x00, 0x1F, 0x32}; -/// Additional block tag ids in the Beacon frames -enum class TagId : u8 { - SSID = 0, - SupportedRates = 1, - DSParameterSet = 2, - TrafficIndicationMap = 5, - CountryInformation = 7, - ERPInformation = 42, - VendorSpecific = 221 -}; - /** * Internal vendor-specific tag ids as stored inside * VendorSpecific blocks in the Beacon frames. diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp new file mode 100644 index 000000000..c8a76ec2a --- /dev/null +++ b/src/core/hle/service/nwm/uds_connection.cpp @@ -0,0 +1,79 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_connection.h" +#include "fmt/format.h" + +namespace Service { +namespace NWM { + +// Note: These values were taken from a packet capture of an o3DS XL +// broadcasting a Super Smash Bros. 4 lobby. +constexpr u16 DefaultExtraCapabilities = 0x0431; + +std::vector GenerateAuthenticationFrame(AuthenticationSeq seq) { + AuthenticationFrame frame{}; + frame.auth_seq = static_cast(seq); + + std::vector data(sizeof(frame)); + std::memcpy(data.data(), &frame, sizeof(frame)); + + return data; +} + +AuthenticationSeq GetAuthenticationSeqNumber(const std::vector& body) { + AuthenticationFrame frame; + std::memcpy(&frame, body.data(), sizeof(frame)); + + return static_cast(frame.auth_seq); +} + +/** + * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte character representation of the + * specified network id as the SSID value. + * @param network_id The network id to use. + * @returns A buffer with the SSID tag. + */ +static std::vector GenerateSSIDTag(u32 network_id) { + constexpr u8 SSIDSize = 8; + + struct { + u8 id = static_cast(TagId::SSID); + u8 size = SSIDSize; + } tag_header; + + std::vector buffer(sizeof(tag_header) + SSIDSize); + + std::memcpy(buffer.data(), &tag_header, sizeof(tag_header)); + + std::string network_name = fmt::format("{0:08X}", network_id); + + std::memcpy(buffer.data() + sizeof(tag_header), network_name.c_str(), SSIDSize); + + return buffer; +} + +std::vector GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id) { + AssociationResponseFrame frame{}; + frame.capabilities = DefaultExtraCapabilities; + frame.status_code = static_cast(status); + // The association id is ORed with this magic value (0xC000) + constexpr u16 AssociationIdMagic = 0xC000; + frame.assoc_id = association_id | AssociationIdMagic; + + std::vector data(sizeof(frame)); + std::memcpy(data.data(), &frame, sizeof(frame)); + + auto ssid_tag = GenerateSSIDTag(network_id); + data.insert(data.end(), ssid_tag.begin(), ssid_tag.end()); + + // TODO(Subv): Add the SupportedRates tag. + // TODO(Subv): Add the DSParameterSet tag. + // TODO(Subv): Add the ERPInformation tag. + return data; +} + +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h new file mode 100644 index 000000000..73f55a4fd --- /dev/null +++ b/src/core/hle/service/nwm/uds_connection.h @@ -0,0 +1,51 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +/// Sequence number of the 802.11 authentication frames. +enum class AuthenticationSeq : u16 { SEQ1 = 1, SEQ2 = 2 }; + +enum class AuthAlgorithm : u16 { OpenSystem = 0 }; + +enum class AuthStatus : u16 { Successful = 0 }; + +enum class AssocStatus : u16 { Successful = 0 }; + +struct AuthenticationFrame { + u16_le auth_algorithm = static_cast(AuthAlgorithm::OpenSystem); + u16_le auth_seq; + u16_le status_code = static_cast(AuthStatus::Successful); +}; + +static_assert(sizeof(AuthenticationFrame) == 6, "AuthenticationFrame has wrong size"); + +struct AssociationResponseFrame { + u16_le capabilities; + u16_le status_code; + u16_le assoc_id; +}; + +static_assert(sizeof(AssociationResponseFrame) == 6, "AssociationResponseFrame has wrong size"); + +/// Generates an 802.11 authentication frame, starting at the frame body. +std::vector GenerateAuthenticationFrame(AuthenticationSeq seq); + +/// Returns the sequence number from the body of an Authentication frame. +AuthenticationSeq GetAuthenticationSeqNumber(const std::vector& body); + +/// Generates an 802.11 association response frame with the specified status, association id and +/// network id, starting at the frame body. +std::vector GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); + +} // namespace NWM +} // namespace Service -- cgit v1.2.3 From 2e9f544ecc9a01ff59859b43d65c61a2838e7c34 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 17 Jul 2017 09:39:12 -0500 Subject: Services/UDS: Store the received beacon frames until RecvBeaconBroadcastData is called, up to 15 beacons at the same time, removing any older beacon frames when the limit is exceeded. --- src/core/hle/service/nwm/nwm_uds.cpp | 65 ++++++++++++++++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 6dbdff044..8fdf160ff 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include "common/common_types.h" @@ -15,8 +16,10 @@ #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_connection.h" #include "core/hle/service/nwm/uds_data.h" #include "core/memory.h" +#include "network/network.h" namespace Service { namespace NWM { @@ -51,6 +54,52 @@ static NetworkInfo network_info; // Event that will generate and send the 802.11 beacon frames. static int beacon_broadcast_event; +// Mutex to synchronize access to the list of received beacons between the emulation thread and the +// network thread. +static std::mutex beacon_mutex; + +// Number of beacons to store before we start dropping the old ones. +// TODO(Subv): Find a more accurate value for this limit. +constexpr size_t MaxBeaconFrames = 15; + +// List of the last beacons received from the network. +static std::deque received_beacons; + +/** + * Returns a list of received 802.11 beacon frames from the specified sender since the last call. + */ +std::deque GetReceivedBeacons(const MacAddress& sender) { + std::lock_guard lock(beacon_mutex); + // TODO(Subv): Filter by sender. + return std::move(received_beacons); +} + +/// Sends a WifiPacket to the room we're currently connected to. +void SendPacket(Network::WifiPacket& packet) { + // TODO(Subv): Implement. +} + +// Inserts the received beacon frame in the beacon queue and removes any older beacons if the size +// limit is exceeded. +void HandleBeaconFrame(const Network::WifiPacket& packet) { + std::lock_guard lock(beacon_mutex); + + received_beacons.emplace_back(packet); + + // Discard old beacons if the buffer is full. + if (received_beacons.size() > MaxBeaconFrames) + received_beacons.pop_front(); +} + +/// Callback to parse and handle a received wifi packet. +void OnWifiPacketReceived(const Network::WifiPacket& packet) { + switch (packet.type) { + case Network::WifiPacket::PacketType::Beacon: + HandleBeaconFrame(packet); + break; + } +} + /** * NWM_UDS::Shutdown service function * Inputs: @@ -111,8 +160,7 @@ static void RecvBeaconBroadcastData(Interface* self) { u32 total_size = sizeof(BeaconDataReplyHeader); // Retrieve all beacon frames that were received from the desired mac address. - std::deque beacons = - GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address); + auto beacons = GetReceivedBeacons(mac_address); BeaconDataReplyHeader data_reply_header{}; data_reply_header.total_entries = beacons.size(); @@ -193,6 +241,9 @@ static void InitializeWithVersion(Interface* self) { rb.Push(RESULT_SUCCESS); rb.PushCopyHandles(Kernel::g_handle_table.Create(connection_status_event).Unwrap()); + // TODO(Subv): Connect the OnWifiPacketReceived function to the wifi packet received callback of + // the room we're currently in. + LOG_DEBUG(Service_NWM, "called sharedmem_size=0x%08X, version=0x%08X, sharedmem_handle=0x%08X", sharedmem_size, version, sharedmem_handle); } @@ -610,9 +661,17 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) return; - // TODO(Subv): Actually send the beacon. std::vector frame = GenerateBeaconFrame(network_info, node_info); + using Network::WifiPacket; + WifiPacket packet; + packet.type = WifiPacket::PacketType::Beacon; + packet.data = std::move(frame); + packet.destination_address = Network::BroadcastMac; + packet.channel = network_channel; + + SendPacket(packet); + // Start broadcasting the network, send a beacon frame every 102.4ms. CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, beacon_broadcast_event, 0); -- cgit v1.2.3 From d088dbfbe1064bb5212e83c50e71e4b2ea5b00cd Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 17 Jul 2017 09:51:03 -0500 Subject: Services/UDS: Handle the connection sequence packets. There is currently no stage tracking, a client is considered "Connected" when it receives the EAPoL Logoff packet from the server, this is not yet implemented. --- src/core/hle/service/nwm/nwm_uds.cpp | 100 +++++++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 17 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 8fdf160ff..893bbb1e7 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -91,12 +91,95 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) { received_beacons.pop_front(); } +/* + * Returns an available index in the nodes array for the + * currently-hosted UDS network. + */ +static u16 GetNextAvailableNodeId() { + ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost), + "Can not accept clients if we're not hosting a network"); + + for (u16 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"); +} + +/* + * Start a connection sequence with an UDS server. The sequence starts by sending an 802.11 + * authentication frame with SEQ1. + */ +void StartConnectionSequence(const MacAddress& server) { + ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); + + // TODO(Subv): Handle timeout. + + // Send an authentication frame with SEQ1 + using Network::WifiPacket; + WifiPacket auth_request; + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); + auth_request.destination_address = server; + auth_request.type = WifiPacket::PacketType::Authentication; + + SendPacket(auth_request); +} + +/// Sends an Association Response frame to the specified mac address +void SendAssociationResponseFrame(const MacAddress& address) { + ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost)); + + using Network::WifiPacket; + WifiPacket assoc_response; + assoc_response.channel = network_channel; + // TODO(Subv): This will cause multiple clients to end up with the same association id, but + // we're not using that for anything. + u16 association_id = 1; + assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, + network_info.network_id); + assoc_response.destination_address = address; + assoc_response.type = WifiPacket::PacketType::AssociationResponse; + + SendPacket(assoc_response); +} + +/* + * Handles the authentication request frame and sends the authentication response and association + * response frames. Once an Authentication frame with SEQ1 is received by the server, it responds + * with an Authentication frame containing SEQ2, and immediately sends an Association response frame + * containing the details of the access point and the assigned association id for the new client. + */ +void HandleAuthenticationFrame(const Network::WifiPacket& packet) { + // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior + if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { + ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost)); + + // Respond with an authentication response frame with SEQ2 + using Network::WifiPacket; + WifiPacket auth_request; + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); + auth_request.destination_address = packet.transmitter_address; + auth_request.type = WifiPacket::PacketType::Authentication; + + SendPacket(auth_request); + + SendAssociationResponseFrame(packet.transmitter_address); + } +} + /// Callback to parse and handle a received wifi packet. void OnWifiPacketReceived(const Network::WifiPacket& packet) { switch (packet.type) { case Network::WifiPacket::PacketType::Beacon: HandleBeaconFrame(packet); break; + case Network::WifiPacket::PacketType::Authentication: + HandleAuthenticationFrame(packet); + break; } } @@ -677,23 +760,6 @@ 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(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. -- cgit v1.2.3 From f64cd87604b7a760e2832c76938d83ec6a284b22 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 17 Jul 2017 09:51:40 -0500 Subject: Services/UDS: Remove an old duplicated declaration of WifiPacket. --- src/core/hle/service/nwm/uds_beacon.cpp | 3 --- src/core/hle/service/nwm/uds_beacon.h | 19 ------------------- 2 files changed, 22 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp index 6332b404c..552eaf65e 100644 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -325,8 +325,5 @@ std::vector GenerateBeaconFrame(const NetworkInfo& network_info, const NodeL return buffer; } -std::deque GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) { - return {}; -} } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h index c726b04d9..50cc76da2 100644 --- a/src/core/hle/service/nwm/uds_beacon.h +++ b/src/core/hle/service/nwm/uds_beacon.h @@ -124,20 +124,6 @@ struct BeaconData { static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); -/// Information about a received WiFi packet. -/// Acts as our own 802.11 header. -struct WifiPacket { - enum class PacketType { Beacon, Data }; - - PacketType type; ///< The type of 802.11 frame, Beacon / Data. - - /// Raw 802.11 frame data, starting at the management frame header for management frames. - std::vector data; - MacAddress transmitter_address; ///< Mac address of the transmitter. - MacAddress destination_address; ///< Mac address of the receiver. - u8 channel; ///< WiFi channel where this frame was transmitted. -}; - /** * Decrypts the beacon data buffer for the network described by `network_info`. */ @@ -150,10 +136,5 @@ void DecryptBeaconData(const NetworkInfo& network_info, std::vector& buffer) */ std::vector GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); -/** - * Returns a list of received 802.11 frames from the specified sender - * matching the type since the last call. - */ -std::deque GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender); } // namespace NWM } // namespace Service -- cgit v1.2.3 From 826606479682234c98e4dfa6e616e637a28d4fcc Mon Sep 17 00:00:00 2001 From: danzel Date: Tue, 29 Aug 2017 20:39:55 +1200 Subject: Use recursive_mutex instead of mutex to fix #2902 --- src/core/hle/lock.cpp | 2 +- src/core/hle/lock.h | 2 +- src/core/hle/svc.cpp | 2 +- src/core/memory.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/lock.cpp b/src/core/hle/lock.cpp index 082f689c8..1c24c7ce9 100644 --- a/src/core/hle/lock.cpp +++ b/src/core/hle/lock.cpp @@ -7,5 +7,5 @@ #include namespace HLE { -std::mutex g_hle_lock; +std::recursive_mutex g_hle_lock; } diff --git a/src/core/hle/lock.h b/src/core/hle/lock.h index 8265621e1..5c99fe996 100644 --- a/src/core/hle/lock.h +++ b/src/core/hle/lock.h @@ -14,5 +14,5 @@ namespace HLE { * to the emulated memory is not protected by this mutex, and should be avoided in any threads other * than the CPU thread. */ -extern std::mutex g_hle_lock; +extern std::recursive_mutex g_hle_lock; } // namespace HLE diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index b98938cb4..dfc36748c 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -1334,7 +1334,7 @@ void CallSVC(u32 immediate) { MICROPROFILE_SCOPE(Kernel_SVC); // Lock the global kernel mutex when we enter the kernel HLE. - std::lock_guard lock(HLE::g_hle_lock); + std::lock_guard lock(HLE::g_hle_lock); const FunctionDef* info = GetSVCInfo(immediate); if (info) { diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a3c5f4a9d..097bc5b47 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -183,7 +183,7 @@ T Read(const VAddr vaddr) { } // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state - std::lock_guard lock(HLE::g_hle_lock); + std::lock_guard lock(HLE::g_hle_lock); PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { @@ -224,7 +224,7 @@ void Write(const VAddr vaddr, const T data) { } // The memory access might do an MMIO or cached access, so we have to lock the HLE kernel state - std::lock_guard lock(HLE::g_hle_lock); + std::lock_guard lock(HLE::g_hle_lock); PageType type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (type) { -- cgit v1.2.3 From 59a9aaf388b71444116a42fe97a969947230908e Mon Sep 17 00:00:00 2001 From: wwylele Date: Wed, 2 Aug 2017 22:56:44 +0300 Subject: APT: load different shared font depending on the region --- src/core/hle/service/apt/apt.cpp | 286 +++++++++++++++++++++------------------ src/core/hle/service/cfg/cfg.cpp | 2 +- src/core/hle/service/cfg/cfg.h | 2 + 3 files changed, 155 insertions(+), 135 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 58d94768c..8c0ba73f2 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -19,6 +19,7 @@ #include "core/hle/service/apt/apt_s.h" #include "core/hle/service/apt/apt_u.h" #include "core/hle/service/apt/bcfnt/bcfnt.h" +#include "core/hle/service/cfg/cfg.h" #include "core/hle/service/fs/archive.h" #include "core/hle/service/ptm/ptm.h" #include "core/hle/service/service.h" @@ -198,6 +199,143 @@ void Initialize(Service::Interface* self) { Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); } +static u32 DecompressLZ11(const u8* in, u8* out) { + u32_le decompressed_size; + memcpy(&decompressed_size, in, sizeof(u32)); + in += 4; + + u8 type = decompressed_size & 0xFF; + ASSERT(type == 0x11); + decompressed_size >>= 8; + + u32 current_out_size = 0; + u8 flags = 0, mask = 1; + while (current_out_size < decompressed_size) { + if (mask == 1) { + flags = *(in++); + mask = 0x80; + } else { + mask >>= 1; + } + + if (flags & mask) { + u8 byte1 = *(in++); + u32 length = byte1 >> 4; + u32 offset; + if (length == 0) { + u8 byte2 = *(in++); + u8 byte3 = *(in++); + length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; + offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; + } else if (length == 1) { + u8 byte2 = *(in++); + u8 byte3 = *(in++); + u8 byte4 = *(in++); + length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; + offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; + } else { + u8 byte2 = *(in++); + length = (byte1 >> 4) + 0x1; + offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; + } + + for (u32 i = 0; i < length; i++) { + *out = *(out - offset); + ++out; + } + + current_out_size += length; + } else { + *(out++) = *(in++); + current_out_size++; + } + } + return decompressed_size; +} + +static bool LoadSharedFont() { + u8 font_region_code; + switch (CFG::GetRegionValue()) { + case 4: // CHN + font_region_code = 2; + break; + case 5: // KOR + font_region_code = 3; + break; + case 6: // TWN + font_region_code = 4; + break; + default: // JPN/EUR/USA + font_region_code = 1; + break; + } + + const u64_le shared_font_archive_id_low = 0x0004009b00014002 | ((font_region_code - 1) << 8); + const u64_le shared_font_archive_id_high = 0x00000001ffffff00; + std::vector shared_font_archive_id(16); + std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); + std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); + FileSys::Path archive_path(shared_font_archive_id); + auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); + if (archive_result.Failed()) + return false; + + std::vector romfs_path(20, 0); // 20-byte all zero path for opening RomFS + FileSys::Path file_path(romfs_path); + FileSys::Mode open_mode = {}; + open_mode.read_flag.Assign(1); + auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); + if (file_result.Failed()) + return false; + + auto romfs = std::move(file_result).Unwrap(); + std::vector romfs_buffer(romfs->backend->GetSize()); + romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); + romfs->backend->Close(); + + const char16_t* file_name[4] = {u"cbf_std.bcfnt.lz", u"cbf_zh-Hans-CN.bcfnt.lz", + u"cbf_ko-Hang-KR.bcfnt.lz", u"cbf_zh-Hant-TW.bcfnt.lz"}; + const u8* font_file = + RomFS::GetFilePointer(romfs_buffer.data(), {file_name[font_region_code - 1]}); + if (font_file == nullptr) + return false; + + struct { + u32_le status; + u32_le region; + u32_le decompressed_size; + INSERT_PADDING_WORDS(0x1D); + } shared_font_header{}; + static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); + + shared_font_header.status = 2; // successfully loaded + shared_font_header.region = font_region_code; + shared_font_header.decompressed_size = + DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); + std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); + *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU" + + return true; +} + +static bool LoadLegacySharedFont() { + // This is the legacy method to load shared font. + // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header + // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided + // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file + // "shared_font.bin" in the Citra "sysdata" directory. + std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; + + FileUtil::CreateFullPath(filepath); // Create path if not already created + FileUtil::IOFile file(filepath, "rb"); + if (file.IsOpen()) { + file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); + return true; + } + + return false; +} + void GetSharedFont(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x44, 0, 0); // 0x00440000 IPC::RequestBuilder rb = rp.MakeBuilder(2, 2); @@ -206,11 +344,20 @@ void GetSharedFont(Service::Interface* self) { Core::Telemetry().AddField(Telemetry::FieldType::Session, "RequiresSharedFont", true); if (!shared_font_loaded) { - LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); - rb.Push(-1); // TODO: Find the right error code - rb.Skip(1 + 2, true); - Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); - return; + // On real 3DS, font loading happens on booting. However, we load it on demand to coordinate + // with CFG region auto configuration, which happens later than APT initialization. + if (LoadSharedFont()) { + shared_font_loaded = true; + } else if (LoadLegacySharedFont()) { + LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); + shared_font_loaded = true; + } else { + LOG_ERROR(Service_APT, "shared font file missing - go dump it from your 3ds"); + rb.Push(-1); // TODO: Find the right error code + rb.Skip(1 + 2, true); + Core::System::GetInstance().SetStatus(Core::System::ResultStatus::ErrorSharedFont); + return; + } } // The shared font has to be relocated to the new address before being passed to the @@ -863,125 +1010,6 @@ void CheckNew3DS(Service::Interface* self) { LOG_WARNING(Service_APT, "(STUBBED) called"); } -static u32 DecompressLZ11(const u8* in, u8* out) { - u32_le decompressed_size; - memcpy(&decompressed_size, in, sizeof(u32)); - in += 4; - - u8 type = decompressed_size & 0xFF; - ASSERT(type == 0x11); - decompressed_size >>= 8; - - u32 current_out_size = 0; - u8 flags = 0, mask = 1; - while (current_out_size < decompressed_size) { - if (mask == 1) { - flags = *(in++); - mask = 0x80; - } else { - mask >>= 1; - } - - if (flags & mask) { - u8 byte1 = *(in++); - u32 length = byte1 >> 4; - u32 offset; - if (length == 0) { - u8 byte2 = *(in++); - u8 byte3 = *(in++); - length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; - offset = (((byte2 & 0x0F) << 8) | byte3) + 0x1; - } else if (length == 1) { - u8 byte2 = *(in++); - u8 byte3 = *(in++); - u8 byte4 = *(in++); - length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; - offset = (((byte3 & 0x0F) << 8) | byte4) + 0x1; - } else { - u8 byte2 = *(in++); - length = (byte1 >> 4) + 0x1; - offset = (((byte1 & 0x0F) << 8) | byte2) + 0x1; - } - - for (u32 i = 0; i < length; i++) { - *out = *(out - offset); - ++out; - } - - current_out_size += length; - } else { - *(out++) = *(in++); - current_out_size++; - } - } - return decompressed_size; -} - -static bool LoadSharedFont() { - // TODO (wwylele): load different font archive for region CHN/KOR/TWN - const u64_le shared_font_archive_id_low = 0x0004009b00014002; - const u64_le shared_font_archive_id_high = 0x00000001ffffff00; - std::vector shared_font_archive_id(16); - std::memcpy(&shared_font_archive_id[0], &shared_font_archive_id_low, sizeof(u64)); - std::memcpy(&shared_font_archive_id[8], &shared_font_archive_id_high, sizeof(u64)); - FileSys::Path archive_path(shared_font_archive_id); - auto archive_result = Service::FS::OpenArchive(Service::FS::ArchiveIdCode::NCCH, archive_path); - if (archive_result.Failed()) - return false; - - std::vector romfs_path(20, 0); // 20-byte all zero path for opening RomFS - FileSys::Path file_path(romfs_path); - FileSys::Mode open_mode = {}; - open_mode.read_flag.Assign(1); - auto file_result = Service::FS::OpenFileFromArchive(*archive_result, file_path, open_mode); - if (file_result.Failed()) - return false; - - auto romfs = std::move(file_result).Unwrap(); - std::vector romfs_buffer(romfs->backend->GetSize()); - romfs->backend->Read(0, romfs_buffer.size(), romfs_buffer.data()); - romfs->backend->Close(); - - const u8* font_file = RomFS::GetFilePointer(romfs_buffer.data(), {u"cbf_std.bcfnt.lz"}); - if (font_file == nullptr) - return false; - - struct { - u32_le status; - u32_le region; - u32_le decompressed_size; - INSERT_PADDING_WORDS(0x1D); - } shared_font_header{}; - static_assert(sizeof(shared_font_header) == 0x80, "shared_font_header has incorrect size"); - - shared_font_header.status = 2; // successfully loaded - shared_font_header.region = 1; // region JPN/EUR/USA - shared_font_header.decompressed_size = - DecompressLZ11(font_file, shared_font_mem->GetPointer(0x80)); - std::memcpy(shared_font_mem->GetPointer(), &shared_font_header, sizeof(shared_font_header)); - *shared_font_mem->GetPointer(0x83) = 'U'; // Change the magic from "CFNT" to "CFNU" - - return true; -} - -static bool LoadLegacySharedFont() { - // This is the legacy method to load shared font. - // The expected format is a decrypted, uncompressed BCFNT file with the 0x80 byte header - // generated by the APT:U service. The best way to get is by dumping it from RAM. We've provided - // a homebrew app to do this: https://github.com/citra-emu/3dsutils. Put the resulting file - // "shared_font.bin" in the Citra "sysdata" directory. - std::string filepath = FileUtil::GetUserPath(D_SYSDATA_IDX) + SHARED_FONT; - - FileUtil::CreateFullPath(filepath); // Create path if not already created - FileUtil::IOFile file(filepath, "rb"); - if (file.IsOpen()) { - file.ReadBytes(shared_font_mem->GetPointer(), file.GetSize()); - return true; - } - - return false; -} - void Init() { AddService(new APT_A_Interface); AddService(new APT_S_Interface); @@ -995,16 +1023,6 @@ void Init() { MemoryPermission::ReadWrite, MemoryPermission::Read, 0, Kernel::MemoryRegion::SYSTEM, "APT:SharedFont"); - if (LoadSharedFont()) { - shared_font_loaded = true; - } else if (LoadLegacySharedFont()) { - LOG_WARNING(Service_APT, "Loaded shared font by legacy method"); - shared_font_loaded = true; - } else { - LOG_WARNING(Service_APT, "Unable to load shared font"); - shared_font_loaded = false; - } - lock = Kernel::Mutex::Create(false, "APT_U:Lock"); cpu_percent = 0; diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 3dbeb27cc..f26a1f65f 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -168,7 +168,7 @@ void GetCountryCodeID(Service::Interface* self) { cmd_buff[2] = country_code_id; } -static u32 GetRegionValue() { +u32 GetRegionValue() { if (Settings::values.region_value == Settings::REGION_VALUE_AUTO_SELECT) return preferred_region_code; diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 1659ebf32..282b6936b 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -101,6 +101,8 @@ void GetCountryCodeString(Service::Interface* self); */ void GetCountryCodeID(Service::Interface* self); +u32 GetRegionValue(); + /** * CFG::SecureInfoGetRegion service function * Inputs: -- cgit v1.2.3 From 589babbf7477423457dddbefbbb29623fa5c0624 Mon Sep 17 00:00:00 2001 From: mailwl Date: Sat, 12 Aug 2017 11:10:04 +0300 Subject: Mii Selector Applet: update Mii structures --- src/core/hle/applets/mii_selector.cpp | 6 ++-- src/core/hle/applets/mii_selector.h | 57 ++++++++++++++++------------------- 2 files changed, 29 insertions(+), 34 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/applets/mii_selector.cpp b/src/core/hle/applets/mii_selector.cpp index 705859f1e..f225c23a5 100644 --- a/src/core/hle/applets/mii_selector.cpp +++ b/src/core/hle/applets/mii_selector.cpp @@ -66,7 +66,7 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa // continue. MiiResult result; memset(&result, 0, sizeof(result)); - result.result_code = 0; + result.return_code = 0; // Let the application know that we're closing Service::APT::MessageParameter message; @@ -82,5 +82,5 @@ ResultCode MiiSelector::StartImpl(const Service::APT::AppletStartupParameter& pa } void MiiSelector::Update() {} -} -} // namespace +} // namespace Applets +} // namespace HLE diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h index ec00e29d2..69db401d0 100644 --- a/src/core/hle/applets/mii_selector.h +++ b/src/core/hle/applets/mii_selector.h @@ -16,51 +16,46 @@ namespace HLE { namespace Applets { struct MiiConfig { - u8 unk_000; - u8 unk_001; - u8 unk_002; - u8 unk_003; - u8 unk_004; + u8 cancel_button_flag; + u8 enable_guest_mii_flag; + u8 show_on_top_screen_flag; + INSERT_PADDING_BYTES(5); + u16 title[0x40]; + INSERT_PADDING_BYTES(4); + u8 show_guest_miis_flag; INSERT_PADDING_BYTES(3); - u16 unk_008; - INSERT_PADDING_BYTES(0x82); - u8 unk_08C; - INSERT_PADDING_BYTES(3); - u16 unk_090; + u32 initially_selected_mii_index; + u8 guest_mii_whitelist[6]; + u8 user_mii_whitelist[0x64]; INSERT_PADDING_BYTES(2); - u32 unk_094; - u16 unk_098; - u8 unk_09A[0x64]; - u8 unk_0FE; - u8 unk_0FF; - u32 unk_100; + u32 magic_value; }; - static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect size"); #define ASSERT_REG_POSITION(field_name, position) \ static_assert(offsetof(MiiConfig, field_name) == position, \ "Field " #field_name " has invalid position") -ASSERT_REG_POSITION(unk_008, 0x08); -ASSERT_REG_POSITION(unk_08C, 0x8C); -ASSERT_REG_POSITION(unk_090, 0x90); -ASSERT_REG_POSITION(unk_094, 0x94); -ASSERT_REG_POSITION(unk_0FE, 0xFE); +ASSERT_REG_POSITION(title, 0x08); +ASSERT_REG_POSITION(show_guest_miis_flag, 0x8C); +ASSERT_REG_POSITION(initially_selected_mii_index, 0x90); +ASSERT_REG_POSITION(guest_mii_whitelist, 0x94); #undef ASSERT_REG_POSITION struct MiiResult { - u32 result_code; - u8 unk_04; - INSERT_PADDING_BYTES(7); - u8 unk_0C[0x60]; - u8 unk_6C[0x16]; + u32 return_code; + u32 guest_mii_selected_flag; + u32 selected_guest_mii_index; + // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii + u8 selected_mii_data[0x5C]; INSERT_PADDING_BYTES(2); + u16 mii_data_checksum; + u16 guest_mii_name[0xC]; }; static_assert(sizeof(MiiResult) == 0x84, "MiiResult structure has incorrect size"); #define ASSERT_REG_POSITION(field_name, position) \ static_assert(offsetof(MiiResult, field_name) == position, \ "Field " #field_name " has invalid position") -ASSERT_REG_POSITION(unk_0C, 0x0C); -ASSERT_REG_POSITION(unk_6C, 0x6C); +ASSERT_REG_POSITION(selected_mii_data, 0x0C); +ASSERT_REG_POSITION(guest_mii_name, 0x6C); #undef ASSERT_REG_POSITION class MiiSelector final : public Applet { @@ -79,5 +74,5 @@ private: MiiConfig config; }; -} -} // namespace +} // namespace Applets +} // namespace HLE -- cgit v1.2.3 From 11f2eff17df600c57aba35384c9f82490368735d Mon Sep 17 00:00:00 2001 From: mailwl Date: Mon, 4 Sep 2017 12:15:15 +0300 Subject: Remove _flag in var names --- src/core/hle/applets/mii_selector.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/applets/mii_selector.h b/src/core/hle/applets/mii_selector.h index 69db401d0..136ce8948 100644 --- a/src/core/hle/applets/mii_selector.h +++ b/src/core/hle/applets/mii_selector.h @@ -16,13 +16,13 @@ namespace HLE { namespace Applets { struct MiiConfig { - u8 cancel_button_flag; - u8 enable_guest_mii_flag; - u8 show_on_top_screen_flag; + u8 enable_cancel_button; + u8 enable_guest_mii; + u8 show_on_top_screen; INSERT_PADDING_BYTES(5); u16 title[0x40]; INSERT_PADDING_BYTES(4); - u8 show_guest_miis_flag; + u8 show_guest_miis; INSERT_PADDING_BYTES(3); u32 initially_selected_mii_index; u8 guest_mii_whitelist[6]; @@ -35,14 +35,14 @@ static_assert(sizeof(MiiConfig) == 0x104, "MiiConfig structure has incorrect siz static_assert(offsetof(MiiConfig, field_name) == position, \ "Field " #field_name " has invalid position") ASSERT_REG_POSITION(title, 0x08); -ASSERT_REG_POSITION(show_guest_miis_flag, 0x8C); +ASSERT_REG_POSITION(show_guest_miis, 0x8C); ASSERT_REG_POSITION(initially_selected_mii_index, 0x90); ASSERT_REG_POSITION(guest_mii_whitelist, 0x94); #undef ASSERT_REG_POSITION struct MiiResult { u32 return_code; - u32 guest_mii_selected_flag; + u32 is_guest_mii_selected; u32 selected_guest_mii_index; // TODO(mailwl): expand to Mii Format structure: https://www.3dbrew.org/wiki/Mii u8 selected_mii_data[0x5C]; -- cgit v1.2.3 From 6d2734a074f44a24129db850339677d8d7b436aa Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 21:17:57 -0500 Subject: Kernel/Memory: Give each Process its own page table. The loader is in charge of setting the newly created process's page table as the main one during the loading process. --- src/core/core.cpp | 1 - src/core/hle/kernel/vm_manager.cpp | 13 ++++-- src/core/hle/kernel/vm_manager.h | 6 ++- src/core/loader/3dsx.cpp | 1 + src/core/loader/elf.cpp | 1 + src/core/loader/ncch.cpp | 1 + src/core/memory.cpp | 87 ++++++-------------------------------- src/core/memory.h | 60 +++++++++++++++++++++++++- src/core/memory_setup.h | 10 ++--- 9 files changed, 93 insertions(+), 87 deletions(-) (limited to 'src/core') diff --git a/src/core/core.cpp b/src/core/core.cpp index 5332318cf..59b8768e7 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -137,7 +137,6 @@ void System::Reschedule() { } System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { - Memory::InitMemoryMap(); LOG_DEBUG(HW_Memory, "initialized OK"); if (Settings::values.use_cpu_jit) { diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index cef1f7fa8..7a007c065 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -56,6 +56,10 @@ void VMManager::Reset() { initial_vma.size = MAX_ADDRESS; vma_map.emplace(initial_vma.base, initial_vma); + page_table.pointers.fill(nullptr); + page_table.attributes.fill(Memory::PageType::Unmapped); + page_table.cached_res_count.fill(0); + UpdatePageTableForVMA(initial_vma); } @@ -328,16 +332,17 @@ VMManager::VMAIter VMManager::MergeAdjacent(VMAIter iter) { void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { switch (vma.type) { case VMAType::Free: - Memory::UnmapRegion(vma.base, vma.size); + Memory::UnmapRegion(page_table, vma.base, vma.size); break; case VMAType::AllocatedMemoryBlock: - Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_block->data() + vma.offset); + Memory::MapMemoryRegion(page_table, vma.base, vma.size, + vma.backing_block->data() + vma.offset); break; case VMAType::BackingMemory: - Memory::MapMemoryRegion(vma.base, vma.size, vma.backing_memory); + Memory::MapMemoryRegion(page_table, vma.base, vma.size, vma.backing_memory); break; case VMAType::MMIO: - Memory::MapIoRegion(vma.base, vma.size, vma.mmio_handler); + Memory::MapIoRegion(page_table, vma.base, vma.size, vma.mmio_handler); break; } } diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index 38e0d74d0..1302527bb 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -9,6 +9,7 @@ #include #include "common/common_types.h" #include "core/hle/result.h" +#include "core/memory.h" #include "core/mmio.h" namespace Kernel { @@ -102,7 +103,6 @@ struct VirtualMemoryArea { * - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/ */ class VMManager final { - // TODO(yuriks): Make page tables switchable to support multiple VMManagers public: /** * The maximum amount of address space managed by the kernel. Addresses above this are never @@ -184,6 +184,10 @@ public: /// Dumps the address space layout to the log, for debugging void LogLayout(Log::Level log_level) const; + /// Each VMManager has its own page table, which is set as the main one when the owning process + /// is scheduled. + Memory::PageTable page_table; + private: using VMAIter = decltype(vma_map)::iterator; diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 74e336487..69cdc0867 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -270,6 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() { Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index cfcde9167..2f27606a1 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -397,6 +397,7 @@ ResultStatus AppLoader_ELF::Load() { Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 7aff7f29b..79ea50147 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -172,6 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() { codeset->memory = std::make_shared>(std::move(code)); Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // Attach a resource limit to the process based on the resource limit category Kernel::g_current_process->resource_limit = diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 65649d9d7..ea46b6ead 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -11,75 +11,18 @@ #include "core/hle/kernel/process.h" #include "core/memory.h" #include "core/memory_setup.h" -#include "core/mmio.h" #include "video_core/renderer_base.h" #include "video_core/video_core.h" namespace Memory { -enum class PageType { - /// Page is unmapped and should cause an access error. - Unmapped, - /// Page is mapped to regular memory. This is the only type you can get pointers to. - Memory, - /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and - /// invalidation - RasterizerCachedMemory, - /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. - Special, - /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and - /// invalidation - RasterizerCachedSpecial, -}; - -struct SpecialRegion { - VAddr base; - u32 size; - MMIORegionPointer handler; -}; - -/** - * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely - * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and - * fetching requirements when accessing. In the usual case of an access to regular memory, it only - * requires an indexed fetch and a check for NULL. - */ -struct PageTable { - /** - * Array of memory pointers backing each page. An entry can only be non-null if the - * corresponding entry in the `attributes` array is of type `Memory`. - */ - std::array pointers; - - /** - * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of - * type `Special`. - */ - std::vector special_regions; - - /** - * Array of fine grained page attributes. If it is set to any value other than `Memory`, then - * the corresponding entry in `pointers` MUST be set to null. - */ - std::array attributes; - - /** - * Indicates the number of externally cached resources touching a page that should be - * flushed before the memory is accessed - */ - std::array cached_res_count; -}; - -/// Singular page table used for the singleton process -static PageTable main_page_table; -/// Currently active page table -static PageTable* current_page_table = &main_page_table; +PageTable* current_page_table = nullptr; std::array* GetCurrentPageTablePointers() { return ¤t_page_table->pointers; } -static void MapPages(u32 base, u32 size, u8* memory, PageType type) { +static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); @@ -90,9 +33,9 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { while (base != end) { ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at %08X", base); - current_page_table->attributes[base] = type; - current_page_table->pointers[base] = memory; - current_page_table->cached_res_count[base] = 0; + page_table.attributes[base] = type; + page_table.pointers[base] = memory; + page_table.cached_res_count[base] = 0; base += 1; if (memory != nullptr) @@ -100,30 +43,24 @@ static void MapPages(u32 base, u32 size, u8* memory, PageType type) { } } -void InitMemoryMap() { - main_page_table.pointers.fill(nullptr); - main_page_table.attributes.fill(PageType::Unmapped); - main_page_table.cached_res_count.fill(0); -} - -void MapMemoryRegion(VAddr base, u32 size, u8* target) { +void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); - MapPages(base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); + MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, target, PageType::Memory); } -void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler) { +void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); - MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); + MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Special); - current_page_table->special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); + page_table.special_regions.emplace_back(SpecialRegion{base, size, mmio_handler}); } -void UnmapRegion(VAddr base, u32 size) { +void UnmapRegion(PageTable& page_table, VAddr base, u32 size) { ASSERT_MSG((size & PAGE_MASK) == 0, "non-page aligned size: %08X", size); ASSERT_MSG((base & PAGE_MASK) == 0, "non-page aligned base: %08X", base); - MapPages(base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); + MapPages(page_table, base / PAGE_SIZE, size / PAGE_SIZE, nullptr, PageType::Unmapped); } /** diff --git a/src/core/memory.h b/src/core/memory.h index c8c56babd..859a14202 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -7,8 +7,10 @@ #include #include #include +#include #include #include "common/common_types.h" +#include "core/mmio.h" namespace Memory { @@ -21,6 +23,59 @@ const u32 PAGE_MASK = PAGE_SIZE - 1; const int PAGE_BITS = 12; const size_t PAGE_TABLE_NUM_ENTRIES = 1 << (32 - PAGE_BITS); +enum class PageType { + /// Page is unmapped and should cause an access error. + Unmapped, + /// Page is mapped to regular memory. This is the only type you can get pointers to. + Memory, + /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and + /// invalidation + RasterizerCachedMemory, + /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions. + Special, + /// Page is mapped to a I/O region, but also needs to check for rasterizer cache flushing and + /// invalidation + RasterizerCachedSpecial, +}; + +struct SpecialRegion { + VAddr base; + u32 size; + MMIORegionPointer handler; +}; + +/** + * A (reasonably) fast way of allowing switchable and remappable process address spaces. It loosely + * mimics the way a real CPU page table works, but instead is optimized for minimal decoding and + * fetching requirements when accessing. In the usual case of an access to regular memory, it only + * requires an indexed fetch and a check for NULL. + */ +struct PageTable { + /** + * Array of memory pointers backing each page. An entry can only be non-null if the + * corresponding entry in the `attributes` array is of type `Memory`. + */ + std::array pointers; + + /** + * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of + * type `Special`. + */ + std::vector special_regions; + + /** + * Array of fine grained page attributes. If it is set to any value other than `Memory`, then + * the corresponding entry in `pointers` MUST be set to null. + */ + std::array attributes; + + /** + * Indicates the number of externally cached resources touching a page that should be + * flushed before the memory is accessed + */ + std::array cached_res_count; +}; + /// Physical memory regions as seen from the ARM11 enum : PAddr { /// IO register area @@ -126,6 +181,9 @@ enum : VAddr { NEW_LINEAR_HEAP_VADDR_END = NEW_LINEAR_HEAP_VADDR + NEW_LINEAR_HEAP_SIZE, }; +/// Currently active page table +extern PageTable* current_page_table; + bool IsValidVirtualAddress(const VAddr addr); bool IsValidPhysicalAddress(const PAddr addr); @@ -209,4 +267,4 @@ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); * retrieve the current page table for that purpose. */ std::array* GetCurrentPageTablePointers(); -} +} // namespace Memory diff --git a/src/core/memory_setup.h b/src/core/memory_setup.h index 3fdf3a87d..c58baa50b 100644 --- a/src/core/memory_setup.h +++ b/src/core/memory_setup.h @@ -9,24 +9,24 @@ namespace Memory { -void InitMemoryMap(); - /** * Maps an allocated buffer onto a region of the emulated process address space. * + * @param page_table The page table of the emulated process. * @param base The address to start mapping at. Must be page-aligned. * @param size The amount of bytes to map. Must be page-aligned. * @param target Buffer with the memory backing the mapping. Must be of length at least `size`. */ -void MapMemoryRegion(VAddr base, u32 size, u8* target); +void MapMemoryRegion(PageTable& page_table, VAddr base, u32 size, u8* target); /** * Maps a region of the emulated process address space as a IO region. + * @param page_table The page table of the emulated process. * @param base The address to start mapping at. Must be page-aligned. * @param size The amount of bytes to map. Must be page-aligned. * @param mmio_handler The handler that backs the mapping. */ -void MapIoRegion(VAddr base, u32 size, MMIORegionPointer mmio_handler); +void MapIoRegion(PageTable& page_table, VAddr base, u32 size, MMIORegionPointer mmio_handler); -void UnmapRegion(VAddr base, u32 size); +void UnmapRegion(PageTable& page_table, VAddr base, u32 size); } -- cgit v1.2.3 From c34ec5e77cd9e83fcf5c929f3951557d5269b7a6 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 21:28:03 -0500 Subject: Kernel/Memory: Switch the current page table when a new process is scheduled. --- src/core/hle/kernel/thread.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'src/core') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index f5f2eb2f7..b7f094f46 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -178,8 +178,18 @@ static void SwitchContext(Thread* new_thread) { Core::CPU().LoadContext(new_thread->context); Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); + + if (!previous_thread || previous_thread->owner_process != current_thread->owner_process) { + Kernel::g_current_process = current_thread->owner_process; + Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; + // We have switched processes and thus, page tables, clear the instruction cache so we + // don't keep stale data from the previous process. + Core::CPU().ClearInstructionCache(); + } } else { current_thread = nullptr; + // Note: We do not reset the current process and current page table when idling because + // technically we haven't changed processes, our threads are just paused. } } -- cgit v1.2.3 From 214150f00c77474927cbdfb1598dbdb2cb4fcf32 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 21 Jul 2017 22:22:59 -0500 Subject: Kernel/Memory: Changed GetPhysicalPointer so that it doesn't go through the current process' page table to obtain a pointer. --- src/core/hle/kernel/memory.cpp | 30 ++++--------------- src/core/hle/kernel/memory.h | 2 ++ src/core/memory.cpp | 65 ++++++++++++++++++++++++++++++++++++++++-- src/core/memory.h | 2 -- 4 files changed, 69 insertions(+), 30 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 496d07cb5..7f27e9655 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -8,7 +8,6 @@ #include #include #include -#include "audio_core/audio_core.h" #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" @@ -24,7 +23,7 @@ namespace Kernel { -static MemoryRegionInfo memory_regions[3]; +MemoryRegionInfo memory_regions[3]; /// Size of the APPLICATION, SYSTEM and BASE memory regions (respectively) for each system /// memory configuration type. @@ -96,9 +95,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { } } -std::array vram; -std::array n3ds_extra_ram; - void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { using namespace Memory; @@ -143,30 +139,14 @@ void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mappin return; } - // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual - // mappings. - u8* target_pointer = nullptr; - switch (area->paddr_base) { - case VRAM_PADDR: - target_pointer = vram.data(); - break; - case DSP_RAM_PADDR: - target_pointer = AudioCore::GetDspMemory().data(); - break; - case N3DS_EXTRA_RAM_PADDR: - target_pointer = n3ds_extra_ram.data(); - break; - default: - UNREACHABLE(); - } + u8* target_pointer = Memory::GetPhysicalPointer(area->paddr_base + offset_into_region); // TODO(yuriks): This flag seems to have some other effect, but it's unknown what MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; - auto vma = address_space - .MapBackingMemory(mapping.address, target_pointer + offset_into_region, - mapping.size, memory_state) - .Unwrap(); + auto vma = + address_space.MapBackingMemory(mapping.address, target_pointer, mapping.size, memory_state) + .Unwrap(); address_space.Reprotect(vma, mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); } diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h index 08c1a9989..da6bb3563 100644 --- a/src/core/hle/kernel/memory.h +++ b/src/core/hle/kernel/memory.h @@ -26,4 +26,6 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); void MapSharedPages(VMManager& address_space); + +extern MemoryRegionInfo memory_regions[3]; } // namespace Kernel diff --git a/src/core/memory.cpp b/src/core/memory.cpp index ea46b6ead..4dcbf2274 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -4,10 +4,12 @@ #include #include +#include "audio_core/audio_core.h" #include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" #include "common/swap.h" +#include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/memory.h" #include "core/memory_setup.h" @@ -16,6 +18,9 @@ namespace Memory { +static std::array vram; +static std::array n3ds_extra_ram; + PageTable* current_page_table = nullptr; std::array* GetCurrentPageTablePointers() { @@ -236,9 +241,63 @@ std::string ReadCString(VAddr vaddr, std::size_t max_length) { } u8* GetPhysicalPointer(PAddr address) { - // TODO(Subv): This call should not go through the application's memory mapping. - boost::optional vaddr = PhysicalToVirtualAddress(address); - return vaddr ? GetPointer(*vaddr) : nullptr; + struct MemoryArea { + PAddr paddr_base; + u32 size; + }; + + static constexpr MemoryArea memory_areas[] = { + {VRAM_PADDR, VRAM_SIZE}, + {IO_AREA_PADDR, IO_AREA_SIZE}, + {DSP_RAM_PADDR, DSP_RAM_SIZE}, + {FCRAM_PADDR, FCRAM_N3DS_SIZE}, + {N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE}, + }; + + const auto area = + std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { + return address >= area.paddr_base && address < area.paddr_base + area.size; + }); + + if (area == std::end(memory_areas)) { + LOG_ERROR(HW_Memory, "unknown GetPhysicalPointer @ 0x%08X", address); + return nullptr; + } + + if (area->paddr_base == IO_AREA_PADDR) { + LOG_ERROR(HW_Memory, "MMIO mappings are not supported yet. phys_addr=0x%08X", address); + return nullptr; + } + + u32 offset_into_region = address - area->paddr_base; + + u8* target_pointer = nullptr; + switch (area->paddr_base) { + case VRAM_PADDR: + target_pointer = vram.data() + offset_into_region; + break; + case DSP_RAM_PADDR: + target_pointer = AudioCore::GetDspMemory().data() + offset_into_region; + break; + case FCRAM_PADDR: + for (const auto& region : Kernel::memory_regions) { + if (offset_into_region >= region.base && + offset_into_region < region.base + region.size) { + target_pointer = + region.linear_heap_memory->data() + offset_into_region - region.base; + break; + } + } + ASSERT_MSG(target_pointer != nullptr, "Invalid FCRAM address"); + break; + case N3DS_EXTRA_RAM_PADDR: + target_pointer = n3ds_extra_ram.data() + offset_into_region; + break; + default: + UNREACHABLE(); + } + + return target_pointer; } void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { diff --git a/src/core/memory.h b/src/core/memory.h index 859a14202..b228a48c2 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -227,8 +227,6 @@ boost::optional PhysicalToVirtualAddress(PAddr addr); /** * Gets a pointer to the memory region beginning at the specified physical address. - * - * @note This is currently implemented using PhysicalToVirtualAddress(). */ u8* GetPhysicalPointer(PAddr address); -- cgit v1.2.3 From b178089251200bd0309afcbcb06b43e7c82dc3bc Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 22 Jul 2017 19:37:26 -0500 Subject: Kernel/Threads: Don't clear the CPU instruction cache when performing a context switch from an idle thread into a thread in the same process. We were unnecessarily clearing the cache when going from Process A -> Idle -> Process A, this caused extreme performance regressions. --- src/core/hle/kernel/thread.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index b7f094f46..f77c39d18 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -171,6 +171,8 @@ static void SwitchContext(Thread* new_thread) { // Cancel any outstanding wakeup events for this thread CoreTiming::UnscheduleEvent(ThreadWakeupEventType, new_thread->callback_handle); + auto previous_process = Kernel::g_current_process; + current_thread = new_thread; ready_queue.remove(new_thread->current_priority, new_thread); @@ -179,7 +181,7 @@ static void SwitchContext(Thread* new_thread) { Core::CPU().LoadContext(new_thread->context); Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); - if (!previous_thread || previous_thread->owner_process != current_thread->owner_process) { + if (previous_process != current_thread->owner_process) { Kernel::g_current_process = current_thread->owner_process; Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; // We have switched processes and thus, page tables, clear the instruction cache so we -- cgit v1.2.3 From f18a176b601c8dc15b372607a4e9f289bdc25ee4 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 7 Aug 2017 13:37:16 -0500 Subject: Kernel/Memory: Make IsValidPhysicalAddress not go through the current process' virtual memory mapping. --- src/core/memory.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4dcbf2274..4d16736f5 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -208,8 +208,7 @@ bool IsValidVirtualAddress(const VAddr vaddr) { } bool IsValidPhysicalAddress(const PAddr paddr) { - boost::optional vaddr = PhysicalToVirtualAddress(paddr); - return vaddr && IsValidVirtualAddress(*vaddr); + return GetPhysicalPointer(paddr) != nullptr; } u8* GetPointer(const VAddr vaddr) { -- cgit v1.2.3 From 7a3ab7c63ddcc79e9dfa46ae0347065f66052105 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 12 Aug 2017 10:16:35 -0500 Subject: CPU/Dynarmic: Disable the fast page-table access in dynarmic until it supports switching page tables at runtime. --- src/core/arm/dynarmic/arm_dynarmic.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 7d2790b08..f2bd0d283 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -56,7 +56,9 @@ static Dynarmic::UserCallbacks GetUserCallbacks( user_callbacks.memory.Write16 = &Memory::Write16; user_callbacks.memory.Write32 = &Memory::Write32; user_callbacks.memory.Write64 = &Memory::Write64; - user_callbacks.page_table = Memory::GetCurrentPageTablePointers(); + // TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at + // runtime. + user_callbacks.page_table = nullptr; user_callbacks.coprocessors[15] = std::make_shared(interpeter_state); return user_callbacks; } -- cgit v1.2.3 From 3d86e3afc4b03037fb1ac8c0b637312a5d0e17f8 Mon Sep 17 00:00:00 2001 From: Subv Date: Mon, 28 Aug 2017 20:26:07 -0500 Subject: Services/NS: Port ns:s to the new service framework. --- src/core/CMakeLists.txt | 6 ++++-- src/core/hle/service/ns/ns.cpp | 16 ++++++++++++++++ src/core/hle/service/ns/ns.h | 16 ++++++++++++++++ src/core/hle/service/ns/ns_s.cpp | 34 ++++++++++++++++++++++++++++++++++ src/core/hle/service/ns/ns_s.h | 21 +++++++++++++++++++++ src/core/hle/service/ns_s.cpp | 33 --------------------------------- src/core/hle/service/ns_s.h | 22 ---------------------- src/core/hle/service/service.cpp | 5 +++-- 8 files changed, 94 insertions(+), 59 deletions(-) create mode 100644 src/core/hle/service/ns/ns.cpp create mode 100644 src/core/hle/service/ns/ns.h create mode 100644 src/core/hle/service/ns/ns_s.cpp create mode 100644 src/core/hle/service/ns/ns_s.h delete mode 100644 src/core/hle/service/ns_s.cpp delete mode 100644 src/core/hle/service/ns_s.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 662030782..89578024f 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -135,7 +135,8 @@ set(SRCS hle/service/nim/nim_aoc.cpp hle/service/nim/nim_s.cpp hle/service/nim/nim_u.cpp - hle/service/ns_s.cpp + hle/service/ns/ns.cpp + hle/service/ns/ns_s.cpp hle/service/nwm/nwm.cpp hle/service/nwm/nwm_cec.cpp hle/service/nwm/nwm_ext.cpp @@ -334,7 +335,8 @@ set(HEADERS hle/service/nim/nim_aoc.h hle/service/nim/nim_s.h hle/service/nim/nim_u.h - hle/service/ns_s.h + hle/service/ns/ns.h + hle/service/ns/ns_s.h hle/service/nwm/nwm.h hle/service/nwm/nwm_cec.h hle/service/nwm/nwm_ext.h diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp new file mode 100644 index 000000000..9e19c38bf --- /dev/null +++ b/src/core/hle/service/ns/ns.cpp @@ -0,0 +1,16 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/ns.h" +#include "core/hle/service/ns/ns_s.h" + +namespace Service { +namespace NS { + +void InstallInterfaces(SM::ServiceManager& service_manager) { + std::make_shared()->InstallAsService(service_manager); +} + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h new file mode 100644 index 000000000..c3d67d98c --- /dev/null +++ b/src/core/hle/service/ns/ns.h @@ -0,0 +1,16 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +/// Registers all NS services with the specified service manager. +void InstallInterfaces(SM::ServiceManager& service_manager); + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns_s.cpp b/src/core/hle/service/ns/ns_s.cpp new file mode 100644 index 000000000..d952888dc --- /dev/null +++ b/src/core/hle/service/ns/ns_s.cpp @@ -0,0 +1,34 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "core/hle/service/ns/ns_s.h" + +namespace Service { +namespace NS { + +NS_S::NS_S() : ServiceFramework("ns:s", 2) { + static const FunctionInfo functions[] = { + {0x000100C0, nullptr, "LaunchFIRM"}, + {0x000200C0, nullptr, "LaunchTitle"}, + {0x00030000, nullptr, "TerminateApplication"}, + {0x00040040, nullptr, "TerminateProcess"}, + {0x000500C0, nullptr, "LaunchApplicationFIRM"}, + {0x00060042, nullptr, "SetFIRMParams4A0"}, + {0x00070042, nullptr, "CardUpdateInitialize"}, + {0x00080000, nullptr, "CardUpdateShutdown"}, + {0x000D0140, nullptr, "SetTWLBannerHMAC"}, + {0x000E0000, nullptr, "ShutdownAsync"}, + {0x00100180, nullptr, "RebootSystem"}, + {0x00110100, nullptr, "TerminateTitle"}, + {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, + {0x00150140, nullptr, "LaunchApplication"}, + {0x00160000, nullptr, "RebootSystemClean"}, + }; + RegisterHandlers(functions); +} + +NS_S::~NS_S() = default; + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns/ns_s.h b/src/core/hle/service/ns/ns_s.h new file mode 100644 index 000000000..660ae453f --- /dev/null +++ b/src/core/hle/service/ns/ns_s.h @@ -0,0 +1,21 @@ +// Copyright 2015 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include "core/hle/kernel/kernel.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NS { + +/// Interface to "ns:s" service +class NS_S final : public ServiceFramework { +public: + NS_S(); + ~NS_S(); +}; + +} // namespace NS +} // namespace Service diff --git a/src/core/hle/service/ns_s.cpp b/src/core/hle/service/ns_s.cpp deleted file mode 100644 index 215c9aacc..000000000 --- a/src/core/hle/service/ns_s.cpp +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include "core/hle/service/ns_s.h" - -namespace Service { -namespace NS { - -const Interface::FunctionInfo FunctionTable[] = { - {0x000100C0, nullptr, "LaunchFIRM"}, - {0x000200C0, nullptr, "LaunchTitle"}, - {0x00030000, nullptr, "TerminateApplication"}, - {0x00040040, nullptr, "TerminateProcess"}, - {0x000500C0, nullptr, "LaunchApplicationFIRM"}, - {0x00060042, nullptr, "SetFIRMParams4A0"}, - {0x00070042, nullptr, "CardUpdateInitialize"}, - {0x00080000, nullptr, "CardUpdateShutdown"}, - {0x000D0140, nullptr, "SetTWLBannerHMAC"}, - {0x000E0000, nullptr, "ShutdownAsync"}, - {0x00100180, nullptr, "RebootSystem"}, - {0x00110100, nullptr, "TerminateTitle"}, - {0x001200C0, nullptr, "SetApplicationCpuTimeLimit"}, - {0x00150140, nullptr, "LaunchApplication"}, - {0x00160000, nullptr, "RebootSystemClean"}, -}; - -NS_S::NS_S() { - Register(FunctionTable); -} - -} // namespace NS -} // namespace Service diff --git a/src/core/hle/service/ns_s.h b/src/core/hle/service/ns_s.h deleted file mode 100644 index 90288a521..000000000 --- a/src/core/hle/service/ns_s.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2015 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include "core/hle/service/service.h" - -namespace Service { -namespace NS { - -class NS_S final : public Interface { -public: - NS_S(); - - std::string GetPortName() const override { - return "ns:s"; - } -}; - -} // namespace NS -} // namespace Service diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp index aad950e50..f267aad74 100644 --- a/src/core/hle/service/service.cpp +++ b/src/core/hle/service/service.cpp @@ -38,7 +38,7 @@ #include "core/hle/service/news/news.h" #include "core/hle/service/nfc/nfc.h" #include "core/hle/service/nim/nim.h" -#include "core/hle/service/ns_s.h" +#include "core/hle/service/ns/ns.h" #include "core/hle/service/nwm/nwm.h" #include "core/hle/service/pm_app.h" #include "core/hle/service/ptm/ptm.h" @@ -215,6 +215,8 @@ void Init() { SM::g_service_manager = std::make_shared(); SM::ServiceManager::InstallInterfaces(SM::g_service_manager); + NS::InstallInterfaces(*SM::g_service_manager); + AddNamedPort(new ERR::ERR_F); FS::ArchiveInit(); @@ -246,7 +248,6 @@ void Init() { AddService(new HTTP::HTTP_C); AddService(new LDR::LDR_RO); AddService(new MIC::MIC_U); - AddService(new NS::NS_S); AddService(new PM::PM_APP); AddService(new SOC::SOC_U); AddService(new SSL::SSL_C); -- cgit v1.2.3 From 28c726f20545744a3052a3e8a0a3bf5ff95a5042 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Tue, 19 Sep 2017 03:18:26 +0200 Subject: WebService: Verify username and token (#2930) * WebService: Verify username and token; Log errors in PostJson * Fixup: added docstrings to the functions * Webservice: Added Icons to the verification, imrpved error detection in cpr, fixup nits * fixup: fmt warning --- src/core/settings.h | 1 + src/core/telemetry_session.cpp | 12 ++++++++++++ src/core/telemetry_session.h | 10 ++++++++++ 3 files changed, 23 insertions(+) (limited to 'src/core') diff --git a/src/core/settings.h b/src/core/settings.h index 024f14666..8d78cb424 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -133,6 +133,7 @@ struct Values { // WebService bool enable_telemetry; std::string telemetry_endpoint_url; + std::string verify_endpoint_url; std::string citra_username; std::string citra_token; } extern values; diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 104a16cc9..ca517ff44 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp @@ -15,6 +15,7 @@ #ifdef ENABLE_WEB_SERVICE #include "web_service/telemetry_json.h" +#include "web_service/verify_login.h" #endif namespace Core { @@ -75,6 +76,17 @@ u64 RegenerateTelemetryId() { return new_telemetry_id; } +std::future VerifyLogin(std::string username, std::string token, std::function func) { +#ifdef ENABLE_WEB_SERVICE + return WebService::VerifyLogin(username, token, Settings::values.verify_endpoint_url, func); +#else + return std::async(std::launch::async, [func{std::move(func)}]() { + func(); + return false; + }); +#endif +} + TelemetrySession::TelemetrySession() { #ifdef ENABLE_WEB_SERVICE if (Settings::values.enable_telemetry) { diff --git a/src/core/telemetry_session.h b/src/core/telemetry_session.h index 65613daae..550c6ea2d 100644 --- a/src/core/telemetry_session.h +++ b/src/core/telemetry_session.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/telemetry.h" @@ -47,4 +48,13 @@ u64 GetTelemetryId(); */ u64 RegenerateTelemetryId(); +/** + * Verifies the username and token. + * @param username Citra username to use for authentication. + * @param token Citra token to use for authentication. + * @param func A function that gets exectued when the verification is finished + * @returns Future with bool indicating whether the verification succeeded + */ +std::future VerifyLogin(std::string username, std::string token, std::function func); + } // namespace Core -- cgit v1.2.3 From 0b33e36292ca44151da32c7866e4c4394add564b Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 24 Sep 2017 00:12:58 -0500 Subject: HLE/SRV: Implemented RegisterService. Now system modules can do more than just crash immediately on startup. --- src/core/hle/service/sm/sm.cpp | 4 ++++ src/core/hle/service/sm/sm.h | 3 +++ src/core/hle/service/sm/srv.cpp | 26 +++++++++++++++++++++++++- src/core/hle/service/sm/srv.h | 1 + 4 files changed, 33 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp index 5e7fc68f9..854ab9a05 100644 --- a/src/core/hle/service/sm/sm.cpp +++ b/src/core/hle/service/sm/sm.cpp @@ -36,6 +36,10 @@ ResultVal> ServiceManager::RegisterService std::string name, unsigned int max_sessions) { CASCADE_CODE(ValidateServiceName(name)); + + if (registered_services.find(name) != registered_services.end()) + return ERR_ALREADY_REGISTERED; + Kernel::SharedPtr server_port; Kernel::SharedPtr client_port; std::tie(server_port, client_port) = Kernel::ServerPort::CreatePortPair(max_sessions, name); diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h index 8f0dbf2db..9f60a7965 100644 --- a/src/core/hle/service/sm/sm.h +++ b/src/core/hle/service/sm/sm.h @@ -32,6 +32,9 @@ constexpr ResultCode ERR_ACCESS_DENIED(6, ErrorModule::SRV, ErrorSummary::Invali ErrorLevel::Permanent); // 0xD8E06406 constexpr ResultCode ERR_NAME_CONTAINS_NUL(7, ErrorModule::SRV, ErrorSummary::WrongArgument, ErrorLevel::Permanent); // 0xD9006407 +constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorDescription::AlreadyExists, ErrorModule::OS, + ErrorSummary::WrongArgument, + ErrorLevel::Permanent); // 0xD9001BFC class ServiceManager { public: diff --git a/src/core/hle/service/sm/srv.cpp b/src/core/hle/service/sm/srv.cpp index 352941e69..5c955cf54 100644 --- a/src/core/hle/service/sm/srv.cpp +++ b/src/core/hle/service/sm/srv.cpp @@ -13,6 +13,7 @@ #include "core/hle/kernel/errors.h" #include "core/hle/kernel/hle_ipc.h" #include "core/hle/kernel/semaphore.h" +#include "core/hle/kernel/server_port.h" #include "core/hle/kernel/server_session.h" #include "core/hle/service/sm/sm.h" #include "core/hle/service/sm/srv.h" @@ -184,12 +185,35 @@ void SRV::PublishToSubscriber(Kernel::HLERequestContext& ctx) { flags); } +void SRV::RegisterService(Kernel::HLERequestContext& ctx) { + IPC::RequestParser rp(ctx, 0x3, 4, 0); + + auto name_buf = rp.PopRaw>(); + size_t name_len = rp.Pop(); + u32 max_sessions = rp.Pop(); + + std::string name(name_buf.data(), std::min(name_len, name_buf.size())); + + auto port = service_manager->RegisterService(name, max_sessions); + + if (port.Failed()) { + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(port.Code()); + LOG_ERROR(Service_SRV, "called service=%s -> error 0x%08X", name.c_str(), port.Code().raw); + return; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushObjects(port.Unwrap()); +} + SRV::SRV(std::shared_ptr service_manager) : ServiceFramework("srv:", 4), service_manager(std::move(service_manager)) { static const FunctionInfo functions[] = { {0x00010002, &SRV::RegisterClient, "RegisterClient"}, {0x00020000, &SRV::EnableNotification, "EnableNotification"}, - {0x00030100, nullptr, "RegisterService"}, + {0x00030100, &SRV::RegisterService, "RegisterService"}, {0x000400C0, nullptr, "UnregisterService"}, {0x00050100, &SRV::GetServiceHandle, "GetServiceHandle"}, {0x000600C2, nullptr, "RegisterPort"}, diff --git a/src/core/hle/service/sm/srv.h b/src/core/hle/service/sm/srv.h index 75cca5184..aad839563 100644 --- a/src/core/hle/service/sm/srv.h +++ b/src/core/hle/service/sm/srv.h @@ -28,6 +28,7 @@ private: void Subscribe(Kernel::HLERequestContext& ctx); void Unsubscribe(Kernel::HLERequestContext& ctx); void PublishToSubscriber(Kernel::HLERequestContext& ctx); + void RegisterService(Kernel::HLERequestContext& ctx); std::shared_ptr service_manager; Kernel::SharedPtr notification_semaphore; -- cgit v1.2.3 From b57d58c0dc94857d28a3ef197d9656f0fbad8e08 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 23 Sep 2017 13:59:07 -0500 Subject: HLE/APT: Prepare the APT Wakeup parameter when the game calls Initialize We need to know what is being run so we can set the APT parameter destination AppId correctly. Delaying the preparation of the parameter until we know which AppId is running lets us support booting both the Home Menu and normal game Applications. --- src/core/hle/service/apt/apt.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 8c0ba73f2..ea964bab1 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -65,6 +65,7 @@ union AppletAttributes { u32 raw; BitField<0, 3, u32> applet_pos; + BitField<29, 1, u32> is_home_menu; AppletAttributes() : raw(0) {} AppletAttributes(u32 attributes) : raw(attributes) {} @@ -158,6 +159,11 @@ static AppletSlotData* GetAppletSlotData(AppletAttributes attributes) { if (slot == AppletSlot::Error) return nullptr; + // The Home Menu is a system applet, however, it has its own applet slot so that it can run + // concurrently with other system applets. + if (slot == AppletSlot::SystemApplet && attributes.is_home_menu) + return &applet_slots[static_cast(AppletSlot::HomeMenu)]; + return &applet_slots[static_cast(slot)]; } @@ -197,6 +203,19 @@ void Initialize(Service::Interface* self) { rb.Push(RESULT_SUCCESS); rb.PushCopyHandles(Kernel::g_handle_table.Create(slot_data->notification_event).Unwrap(), Kernel::g_handle_table.Create(slot_data->parameter_event).Unwrap()); + + if (slot_data->applet_id == AppletId::Application || + slot_data->applet_id == AppletId::HomeMenu) { + // Initialize the APT parameter to wake up the application. + next_parameter.emplace(); + next_parameter->signal = static_cast(SignalType::Wakeup); + next_parameter->sender_id = static_cast(AppletId::None); + next_parameter->destination_id = app_id; + // Not signaling the parameter event will cause the application (or Home Menu) to hang + // during startup. In the real console, it is usually the Kernel and Home Menu who cause NS + // to signal the HomeMenu and Application parameter events, respectively. + slot_data->parameter_event->Signal(); + } } static u32 DecompressLZ11(const u8* in, u8* out) { @@ -1041,12 +1060,6 @@ void Init() { slot_data.parameter_event = Kernel::Event::Create(Kernel::ResetType::OneShot, "APT:Parameter"); } - - // Initialize the parameter to wake up the application. - next_parameter.emplace(); - next_parameter->signal = static_cast(SignalType::Wakeup); - next_parameter->destination_id = static_cast(AppletId::Application); - applet_slots[static_cast(AppletSlot::Application)].parameter_event->Signal(); } void Shutdown() { -- cgit v1.2.3 From 7096f01c14ff76aefd829ae449a8ab5d474eacf7 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 23 Sep 2017 14:01:04 -0500 Subject: HLE/APT: Always return an error from PrepareToStartNewestHomeMenu so that the Home Menu doesn't try to reboot the system. As per 3dbrew: "During Home Menu start-up it uses APT:PrepareToStartNewestHomeMenu. If that doesn't return an error(normally NS returns 0xC8A0CFFC for that), Home Menu starts a hardware reboot with APT:StartNewestHomeMenu etc. " --- src/core/hle/service/apt/apt.cpp | 14 ++++++++++++++ src/core/hle/service/apt/apt.h | 10 ++++++++++ src/core/hle/service/apt/apt_s.cpp | 4 ++-- 3 files changed, 26 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index ea964bab1..490c14605 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -776,6 +776,20 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); } +void PrepareToStartNewestHomeMenu(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); // 0x1A0000 + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + // TODO(Subv): This command can only be called by a System Applet (return 0xC8A0CC04 otherwise). + + // This command must return an error when called, otherwise the Home Menu will try to reboot the + // system. + rb.Push(ResultCode(ErrorDescription::AlreadyExists, ErrorModule::Applet, + ErrorSummary::InvalidState, ErrorLevel::Status)); + + LOG_DEBUG(Service_APT, "called"); +} + void PreloadLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 AppletId applet_id = static_cast(rp.Pop()); diff --git a/src/core/hle/service/apt/apt.h b/src/core/hle/service/apt/apt.h index 96b28b438..7b79e1f3e 100644 --- a/src/core/hle/service/apt/apt.h +++ b/src/core/hle/service/apt/apt.h @@ -419,6 +419,16 @@ void GetAppCpuTimeLimit(Service::Interface* self); */ void PrepareToStartLibraryApplet(Service::Interface* self); +/** + * APT::PrepareToStartNewestHomeMenu service function + * Inputs: + * 0 : Command header [0x001A0000] + * Outputs: + * 0 : Return header + * 1 : Result of function + */ +void PrepareToStartNewestHomeMenu(Service::Interface* self); + /** * APT::PreloadLibraryApplet service function * Inputs: diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index ec5668d05..fe1d21fff 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -17,7 +17,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00060040, GetAppletInfo, "GetAppletInfo"}, {0x00070000, nullptr, "GetLastSignaledAppletId"}, {0x00080000, nullptr, "CountRegisteredApplet"}, - {0x00090040, nullptr, "IsRegistered"}, + {0x00090040, IsRegistered, "IsRegistered"}, {0x000A0040, nullptr, "GetAttribute"}, {0x000B0040, InquireNotification, "InquireNotification"}, {0x000C0104, nullptr, "SendParameter"}, @@ -34,7 +34,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00170040, nullptr, "FinishPreloadingLibraryApplet"}, {0x00180040, PrepareToStartLibraryApplet, "PrepareToStartLibraryApplet"}, {0x00190040, nullptr, "PrepareToStartSystemApplet"}, - {0x001A0000, nullptr, "PrepareToStartNewestHomeMenu"}, + {0x001A0000, PrepareToStartNewestHomeMenu, "PrepareToStartNewestHomeMenu"}, {0x001B00C4, nullptr, "StartApplication"}, {0x001C0000, nullptr, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, -- cgit v1.2.3 From c02bbb7030efd072511bd0051a44d9e503016f74 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Sep 2017 22:42:42 +0100 Subject: memory: Add GetCurrentPageTable/SetCurrentPageTable Don't expose Memory::current_page_table as a global. --- src/core/hle/kernel/thread.cpp | 11 ++++------- src/core/loader/3dsx.cpp | 2 +- src/core/loader/elf.cpp | 2 +- src/core/loader/ncch.cpp | 2 +- src/core/memory.cpp | 10 +++++++++- src/core/memory.h | 3 ++- 6 files changed, 18 insertions(+), 12 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 324415a36..61378211f 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -178,16 +178,13 @@ static void SwitchContext(Thread* new_thread) { ready_queue.remove(new_thread->current_priority, new_thread); new_thread->status = THREADSTATUS_RUNNING; - Core::CPU().LoadContext(new_thread->context); - Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); - if (previous_process != current_thread->owner_process) { Kernel::g_current_process = current_thread->owner_process; - Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; - // We have switched processes and thus, page tables, clear the instruction cache so we - // don't keep stale data from the previous process. - Core::CPU().ClearInstructionCache(); + SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); } + + Core::CPU().LoadContext(new_thread->context); + Core::CPU().SetCP15Register(CP15_THREAD_URO, new_thread->GetTLSAddress()); } else { current_thread = nullptr; // Note: We do not reset the current process and current page table when idling because diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 69cdc0867..a03515e6e 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -270,7 +270,7 @@ ResultStatus AppLoader_THREEDSX::Load() { Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; - Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; + Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 2f27606a1..2de1f4e81 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -397,7 +397,7 @@ ResultStatus AppLoader_ELF::Load() { Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); Kernel::g_current_process->svc_access_mask.set(); Kernel::g_current_process->address_mappings = default_address_mappings; - Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; + Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); // Attach the default resource limit (APPLICATION) to the process Kernel::g_current_process->resource_limit = diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 79ea50147..ca282f935 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -172,7 +172,7 @@ ResultStatus AppLoader_NCCH::LoadExec() { codeset->memory = std::make_shared>(std::move(code)); Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); - Memory::current_page_table = &Kernel::g_current_process->vm_manager.page_table; + Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); // Attach a resource limit to the process based on the resource limit category Kernel::g_current_process->resource_limit = diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 68a6b1ac2..17fa10b49 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -22,12 +22,20 @@ namespace Memory { static std::array vram; static std::array n3ds_extra_ram; -PageTable* current_page_table = nullptr; +static PageTable* current_page_table = nullptr; std::array* GetCurrentPageTablePointers() { return ¤t_page_table->pointers; } +void SetCurrentPageTable(PageTable* page_table) { + current_page_table = page_table; +} + +PageTable* GetCurrentPageTable() { + return current_page_table; +} + static void MapPages(PageTable& page_table, u32 base, u32 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping %p onto %08X-%08X", memory, base * PAGE_SIZE, (base + size) * PAGE_SIZE); diff --git a/src/core/memory.h b/src/core/memory.h index b228a48c2..db5a704d0 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -182,7 +182,8 @@ enum : VAddr { }; /// Currently active page table -extern PageTable* current_page_table; +void SetCurrentPageTable(PageTable* page_table); +PageTable* GetCurrentPageTable(); bool IsValidVirtualAddress(const VAddr addr); bool IsValidPhysicalAddress(const PAddr addr); -- cgit v1.2.3 From 4e5eb2044acc304fc2068b53eb03e3a626832996 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Sep 2017 22:43:28 +0100 Subject: memory: Remove GetCurrentPageTablePointers --- src/core/memory.cpp | 4 ---- src/core/memory.h | 6 ------ 2 files changed, 10 deletions(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 17fa10b49..67ba732ad 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -24,10 +24,6 @@ static std::array n3ds_extra_ram; static PageTable* current_page_table = nullptr; -std::array* GetCurrentPageTablePointers() { - return ¤t_page_table->pointers; -} - void SetCurrentPageTable(PageTable* page_table) { current_page_table = page_table; } diff --git a/src/core/memory.h b/src/core/memory.h index db5a704d0..1865bfea0 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -260,10 +260,4 @@ enum class FlushMode { */ void RasterizerFlushVirtualRegion(VAddr start, u32 size, FlushMode mode); -/** - * Dynarmic has an optimization to memory accesses when the pointer to the page exists that - * can be used by setting up the current page table as a callback. This function is used to - * retrieve the current page table for that purpose. - */ -std::array* GetCurrentPageTablePointers(); } // namespace Memory -- cgit v1.2.3 From 67a70bd9e1655dfd705550c1d561f3ba444360c8 Mon Sep 17 00:00:00 2001 From: MerryMage Date: Sun, 24 Sep 2017 22:44:13 +0100 Subject: ARM_Interface: Implement PageTableChanged --- src/core/arm/arm_interface.h | 3 +++ src/core/arm/dynarmic/arm_dynarmic.cpp | 22 +++++++++++++++++----- src/core/arm/dynarmic/arm_dynarmic.h | 10 +++++++++- src/core/arm/dyncom/arm_dyncom.cpp | 4 ++++ src/core/arm/dyncom/arm_dyncom.h | 1 + src/core/memory.cpp | 5 +++++ 6 files changed, 39 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index ccd43f431..2aa017a54 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -41,6 +41,9 @@ public: /// Clear all instruction cache virtual void ClearInstructionCache() = 0; + /// Notify CPU emulation that page tables have changed + virtual void PageTableChanged() = 0; + /** * Set the Program Counter to an address * @param addr Address to set PC to diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 34c5aa381..42ae93ae8 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -41,7 +41,7 @@ static bool IsReadOnlyMemory(u32 vaddr) { } static Dynarmic::UserCallbacks GetUserCallbacks( - const std::shared_ptr& interpeter_state) { + const std::shared_ptr& interpeter_state, Memory::PageTable* current_page_table) { Dynarmic::UserCallbacks user_callbacks{}; user_callbacks.InterpreterFallback = &InterpreterFallback; user_callbacks.user_arg = static_cast(interpeter_state.get()); @@ -56,16 +56,14 @@ static Dynarmic::UserCallbacks GetUserCallbacks( user_callbacks.memory.Write16 = &Memory::Write16; user_callbacks.memory.Write32 = &Memory::Write32; user_callbacks.memory.Write64 = &Memory::Write64; - // TODO(Subv): Re-add the page table pointers once dynarmic supports switching page tables at - // runtime. - user_callbacks.page_table = nullptr; + user_callbacks.page_table = ¤t_page_table->pointers; user_callbacks.coprocessors[15] = std::make_shared(interpeter_state); return user_callbacks; } ARM_Dynarmic::ARM_Dynarmic(PrivilegeMode initial_mode) { interpreter_state = std::make_shared(initial_mode); - jit = std::make_unique(GetUserCallbacks(interpreter_state)); + PageTableChanged(); } void ARM_Dynarmic::SetPC(u32 pc) { @@ -136,6 +134,7 @@ void ARM_Dynarmic::AddTicks(u64 ticks) { MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { + ASSERT(Memory::GetCurrentPageTable() == current_page_table); MICROPROFILE_SCOPE(ARM_Jit); std::size_t ticks_executed = jit->Run(static_cast(num_instructions)); @@ -178,3 +177,16 @@ void ARM_Dynarmic::PrepareReschedule() { void ARM_Dynarmic::ClearInstructionCache() { jit->ClearCache(); } + +void ARM_Dynarmic::PageTableChanged() { + current_page_table = Memory::GetCurrentPageTable(); + + auto iter = jits.find(current_page_table); + if (iter != jits.end()) { + jit = iter->second.get(); + return; + } + + jit = new Dynarmic::Jit(GetUserCallbacks(interpreter_state, current_page_table)); + jits.emplace(current_page_table, std::unique_ptr(jit)); +} diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 834dc989e..96148a1a5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -4,12 +4,17 @@ #pragma once +#include #include #include #include "common/common_types.h" #include "core/arm/arm_interface.h" #include "core/arm/skyeye_common/armstate.h" +namespace Memory { +struct PageTable; +} // namespace Memory + class ARM_Dynarmic final : public ARM_Interface { public: ARM_Dynarmic(PrivilegeMode initial_mode); @@ -36,8 +41,11 @@ public: void ExecuteInstructions(int num_instructions) override; void ClearInstructionCache() override; + void PageTableChanged() override; private: - std::unique_ptr jit; + Dynarmic::Jit* jit = nullptr; + Memory::PageTable* current_page_table = nullptr; + std::map> jits; std::shared_ptr interpreter_state; }; diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index 81f9bf99e..da955c9b9 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -25,6 +25,10 @@ void ARM_DynCom::ClearInstructionCache() { trans_cache_buf_top = 0; } +void ARM_DynCom::PageTableChanged() { + ClearInstructionCache(); +} + void ARM_DynCom::SetPC(u32 pc) { state->Reg[15] = pc; } diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 62c174f3c..0ae535671 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -16,6 +16,7 @@ public: ~ARM_DynCom(); void ClearInstructionCache() override; + void PageTableChanged() override; void SetPC(u32 pc) override; u32 GetPC() const override; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 67ba732ad..a6b5f6c99 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -9,6 +9,8 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "common/swap.h" +#include "core/arm/arm_interface.h" +#include "core/core.h" #include "core/hle/kernel/memory.h" #include "core/hle/kernel/process.h" #include "core/hle/lock.h" @@ -26,6 +28,9 @@ static PageTable* current_page_table = nullptr; void SetCurrentPageTable(PageTable* page_table) { current_page_table = page_table; + if (Core::System::GetInstance().IsPoweredOn()) { + Core::CPU().PageTableChanged(); + } } PageTable* GetCurrentPageTable() { -- cgit v1.2.3 From d673d508dd1ca463dc72ff68b5582ee56d62f142 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Mon, 25 Sep 2017 08:16:27 +0200 Subject: Services/UDS: Added a function to send EAPoL-Start packets (#2920) * Services/UDS: Added a function to generate the EAPoL-Start packet body. * Services/UDS: Added filter for beacons. * Services/UDS: Lock a mutex when accessing connection_status from both the emulation and network thread. * Services/UDS: Handle the Association Response frame and respond with the EAPoL-Start frame. * fixup: make use of current_node, changed received_beacons into a list, mutex and assert corrections * fixup: fix damn clang-format --- src/core/hle/service/nwm/nwm_uds.cpp | 275 +++++++++++++++++++--------- src/core/hle/service/nwm/uds_connection.cpp | 9 + src/core/hle/service/nwm/uds_connection.h | 5 + src/core/hle/service/nwm/uds_data.cpp | 21 +++ src/core/hle/service/nwm/uds_data.h | 28 +++ 5 files changed, 250 insertions(+), 88 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 893bbb1e7..4e2af9ae6 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -2,8 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include +#include #include #include #include @@ -37,9 +39,12 @@ static ConnectionStatus connection_status{}; /* Node information about the current network. * The amount of elements in this vector is always the maximum number * of nodes specified in the network configuration. - * The first node is always the host, so this always contains at least 1 entry. + * The first node is always the host. */ -static NodeList node_info(1); +static NodeList node_info; + +// Node information about our own system. +static NodeInfo current_node; // Mapping of bind node ids to their respective events. static std::unordered_map> bind_node_events; @@ -54,6 +59,10 @@ static NetworkInfo network_info; // Event that will generate and send the 802.11 beacon frames. static int beacon_broadcast_event; +// Mutex to synchronize access to the connection status between the emulation thread and the +// network thread. +static std::mutex connection_status_mutex; + // Mutex to synchronize access to the list of received beacons between the emulation thread and the // network thread. static std::mutex beacon_mutex; @@ -63,14 +72,26 @@ static std::mutex beacon_mutex; constexpr size_t MaxBeaconFrames = 15; // List of the last beacons received from the network. -static std::deque received_beacons; +static std::list received_beacons; /** * Returns a list of received 802.11 beacon frames from the specified sender since the last call. */ -std::deque GetReceivedBeacons(const MacAddress& sender) { +std::list GetReceivedBeacons(const MacAddress& sender) { std::lock_guard lock(beacon_mutex); - // TODO(Subv): Filter by sender. + if (sender != Network::BroadcastMac) { + std::list filtered_list; + const auto beacon = std::find_if(received_beacons.begin(), received_beacons.end(), + [&sender](const Network::WifiPacket& packet) { + return packet.transmitter_address == sender; + }); + if (beacon != received_beacons.end()) { + filtered_list.push_back(*beacon); + // TODO(B3N30): Check if the complete deque is cleared or just the fetched entries + received_beacons.erase(beacon); + } + return filtered_list; + } return std::move(received_beacons); } @@ -83,6 +104,15 @@ void SendPacket(Network::WifiPacket& packet) { // limit is exceeded. void HandleBeaconFrame(const Network::WifiPacket& packet) { std::lock_guard lock(beacon_mutex); + const auto unique_beacon = + std::find_if(received_beacons.begin(), received_beacons.end(), + [&packet](const Network::WifiPacket& new_packet) { + return new_packet.transmitter_address == packet.transmitter_address; + }); + if (unique_beacon != received_beacons.end()) { + // We already have a beacon from the same mac in the deque, remove the old one; + received_beacons.erase(unique_beacon); + } received_beacons.emplace_back(packet); @@ -91,14 +121,33 @@ void HandleBeaconFrame(const Network::WifiPacket& packet) { received_beacons.pop_front(); } +void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { + auto assoc_result = GetAssociationResult(packet.data); + + ASSERT_MSG(std::get(assoc_result) == AssocStatus::Successful, + "Could not join network"); + { + std::lock_guard lock(connection_status_mutex); + ASSERT(connection_status.status == static_cast(NetworkStatus::Connecting)); + } + + // Send the EAPoL-Start packet to the server. + using Network::WifiPacket; + WifiPacket eapol_start; + eapol_start.channel = network_channel; + eapol_start.data = GenerateEAPoLStartFrame(std::get(assoc_result), current_node); + // TODO(B3N30): Encrypt the packet. + eapol_start.destination_address = packet.transmitter_address; + eapol_start.type = WifiPacket::PacketType::Data; + + SendPacket(eapol_start); +} + /* * Returns an available index in the nodes array for the * currently-hosted UDS network. */ static u16 GetNextAvailableNodeId() { - ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost), - "Can not accept clients if we're not hosting a network"); - for (u16 index = 0; index < connection_status.max_nodes; ++index) { if ((connection_status.node_bitmask & (1 << index)) == 0) return index; @@ -113,35 +162,46 @@ static u16 GetNextAvailableNodeId() { * authentication frame with SEQ1. */ void StartConnectionSequence(const MacAddress& server) { - ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); - - // TODO(Subv): Handle timeout. - - // Send an authentication frame with SEQ1 using Network::WifiPacket; WifiPacket auth_request; - auth_request.channel = network_channel; - auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); - auth_request.destination_address = server; - auth_request.type = WifiPacket::PacketType::Authentication; + { + std::lock_guard lock(connection_status_mutex); + ASSERT(connection_status.status == static_cast(NetworkStatus::NotConnected)); + + // TODO(Subv): Handle timeout. + + // Send an authentication frame with SEQ1 + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ1); + auth_request.destination_address = server; + auth_request.type = WifiPacket::PacketType::Authentication; + } SendPacket(auth_request); } /// Sends an Association Response frame to the specified mac address void SendAssociationResponseFrame(const MacAddress& address) { - ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost)); - using Network::WifiPacket; WifiPacket assoc_response; - assoc_response.channel = network_channel; - // TODO(Subv): This will cause multiple clients to end up with the same association id, but - // we're not using that for anything. - u16 association_id = 1; - assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, - network_info.network_id); - assoc_response.destination_address = address; - assoc_response.type = WifiPacket::PacketType::AssociationResponse; + + { + std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { + LOG_ERROR(Service_NWM, "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + + assoc_response.channel = network_channel; + // TODO(Subv): This will cause multiple clients to end up with the same association id, but + // we're not using that for anything. + u16 association_id = 1; + assoc_response.data = GenerateAssocResponseFrame(AssocStatus::Successful, association_id, + network_info.network_id); + assoc_response.destination_address = address; + assoc_response.type = WifiPacket::PacketType::AssociationResponse; + } SendPacket(assoc_response); } @@ -155,16 +215,23 @@ void SendAssociationResponseFrame(const MacAddress& address) { void HandleAuthenticationFrame(const Network::WifiPacket& packet) { // Only the SEQ1 auth frame is handled here, the SEQ2 frame doesn't need any special behavior if (GetAuthenticationSeqNumber(packet.data) == AuthenticationSeq::SEQ1) { - ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost)); - - // Respond with an authentication response frame with SEQ2 using Network::WifiPacket; WifiPacket auth_request; - auth_request.channel = network_channel; - auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); - auth_request.destination_address = packet.transmitter_address; - auth_request.type = WifiPacket::PacketType::Authentication; - + { + std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { + LOG_ERROR(Service_NWM, + "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + + // Respond with an authentication response frame with SEQ2 + auth_request.channel = network_channel; + auth_request.data = GenerateAuthenticationFrame(AuthenticationSeq::SEQ2); + auth_request.destination_address = packet.transmitter_address; + auth_request.type = WifiPacket::PacketType::Authentication; + } SendPacket(auth_request); SendAssociationResponseFrame(packet.transmitter_address); @@ -180,6 +247,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { case Network::WifiPacket::PacketType::Authentication: HandleAuthenticationFrame(packet); break; + case Network::WifiPacket::PacketType::AssociationResponse: + HandleAssociationResponseFrame(packet); + break; } } @@ -305,7 +375,7 @@ static void InitializeWithVersion(Interface* self) { u32 sharedmem_size = rp.Pop(); // Update the node information with the data the game gave us. - rp.PopRaw(node_info[0]); + rp.PopRaw(current_node); u16 version = rp.Pop(); @@ -315,10 +385,14 @@ static void InitializeWithVersion(Interface* self) { 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(NetworkStatus::NotConnected); + { + std::lock_guard lock(connection_status_mutex); + + // Reset the connection status, it contains all zeros after initialization, + // except for the actual status value. + connection_status = {}; + connection_status.status = static_cast(NetworkStatus::NotConnected); + } IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(RESULT_SUCCESS); @@ -348,12 +422,16 @@ static void GetConnectionStatus(Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(13, 0); rb.Push(RESULT_SUCCESS); - rb.PushRaw(connection_status); - - // Reset the bitmask of changed nodes after each call to this - // function to prevent falsely informing games of outstanding - // changes in subsequent calls. - connection_status.changed_nodes = 0; + { + std::lock_guard lock(connection_status_mutex); + rb.PushRaw(connection_status); + + // Reset the bitmask of changed nodes after each call to this + // function to prevent falsely informing games of outstanding + // changes in subsequent calls. + // TODO(Subv): Find exactly where the NWM module resets this value. + connection_status.changed_nodes = 0; + } LOG_DEBUG(Service_NWM, "called"); } @@ -434,31 +512,36 @@ static void BeginHostingNetwork(Interface* self) { // 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(NetworkStatus::ConnectedAsHost); - - // Ensure the application data size is less than the maximum value. - ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); - - // Set up basic information for this network. - network_info.oui_value = NintendoOUI; - network_info.oui_type = static_cast(NintendoTagId::NetworkInfo); - - connection_status.max_nodes = network_info.max_nodes; - - // Resize the nodes list to hold max_nodes. - node_info.resize(network_info.max_nodes); - - // There's currently only one node in the network (the host). - connection_status.total_nodes = 1; - network_info.total_nodes = 1; - // The host is always the first node - connection_status.network_node_id = 1; - node_info[0].network_node_id = 1; - connection_status.nodes[0] = connection_status.network_node_id; - // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. - connection_status.node_bitmask |= 1; - // Notify the application that the first node was set. - connection_status.changed_nodes |= 1; + { + std::lock_guard lock(connection_status_mutex); + connection_status.status = static_cast(NetworkStatus::ConnectedAsHost); + + // Ensure the application data size is less than the maximum value. + ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, + "Data size is too big."); + + // Set up basic information for this network. + network_info.oui_value = NintendoOUI; + network_info.oui_type = static_cast(NintendoTagId::NetworkInfo); + + connection_status.max_nodes = network_info.max_nodes; + + // Resize the nodes list to hold max_nodes. + node_info.resize(network_info.max_nodes); + + // There's currently only one node in the network (the host). + connection_status.total_nodes = 1; + network_info.total_nodes = 1; + // The host is always the first node + connection_status.network_node_id = 1; + current_node.network_node_id = 1; + connection_status.nodes[0] = connection_status.network_node_id; + // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. + connection_status.node_bitmask |= 1; + // Notify the application that the first node was set. + connection_status.changed_nodes |= 1; + node_info[0] = current_node; + } // If the game has a preferred channel, use that instead. if (network_info.channel != 0) @@ -495,9 +578,13 @@ static void DestroyNetwork(Interface* self) { // Unschedule the beacon broadcast event. CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); - // TODO(Subv): Check if connection_status is indeed reset after this call. - connection_status = {}; - connection_status.status = static_cast(NetworkStatus::NotConnected); + { + std::lock_guard lock(connection_status_mutex); + + // TODO(Subv): Check if connection_status is indeed reset after this call. + connection_status = {}; + connection_status.status = static_cast(NetworkStatus::NotConnected); + } connection_status_event->Signal(); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); @@ -540,17 +627,24 @@ static void SendTo(Interface* self) { IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && - connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { - rb.Push(ResultCode(ErrorDescription::NotAuthorized, ErrorModule::UDS, - ErrorSummary::InvalidState, ErrorLevel::Status)); - return; - } + u16 network_node_id; - if (dest_node_id == connection_status.network_node_id) { - rb.Push(ResultCode(ErrorDescription::NotFound, ErrorModule::UDS, - ErrorSummary::WrongArgument, ErrorLevel::Status)); - return; + { + std::lock_guard lock(connection_status_mutex); + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsClient) && + connection_status.status != static_cast(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; + } + + network_node_id = connection_status.network_node_id; } // TODO(Subv): Do something with the flags. @@ -567,8 +661,8 @@ static void SendTo(Interface* self) { // TODO(Subv): Increment the sequence number after each sent packet. u16 sequence_number = 0; - std::vector data_payload = GenerateDataPayload( - data, data_channel, dest_node_id, connection_status.network_node_id, sequence_number); + std::vector data_payload = + GenerateDataPayload(data, data_channel, dest_node_id, 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. @@ -595,6 +689,7 @@ static void GetChannel(Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1A, 0, 0); IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + std::lock_guard lock(connection_status_mutex); bool is_connected = connection_status.status != static_cast(NetworkStatus::NotConnected); u8 channel = is_connected ? network_channel : 0; @@ -766,6 +861,7 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { * @param network_node_id Network Node Id of the connecting client. */ void OnClientConnected(u16 network_node_id) { + std::lock_guard lock(connection_status_mutex); ASSERT_MSG(connection_status.status == static_cast(NetworkStatus::ConnectedAsHost), "Can not accept clients if we're not hosting a network"); ASSERT_MSG(connection_status.total_nodes < connection_status.max_nodes, @@ -827,8 +923,11 @@ NWM_UDS::~NWM_UDS() { connection_status_event = nullptr; recv_buffer_memory = nullptr; - connection_status = {}; - connection_status.status = static_cast(NetworkStatus::NotConnected); + { + std::lock_guard lock(connection_status_mutex); + connection_status = {}; + connection_status.status = static_cast(NetworkStatus::NotConnected); + } CoreTiming::UnscheduleEvent(beacon_broadcast_event, 0); } diff --git a/src/core/hle/service/nwm/uds_connection.cpp b/src/core/hle/service/nwm/uds_connection.cpp index c8a76ec2a..c74f51253 100644 --- a/src/core/hle/service/nwm/uds_connection.cpp +++ b/src/core/hle/service/nwm/uds_connection.cpp @@ -75,5 +75,14 @@ std::vector GenerateAssocResponseFrame(AssocStatus status, u16 association_i return data; } +std::tuple GetAssociationResult(const std::vector& body) { + AssociationResponseFrame frame; + memcpy(&frame, body.data(), sizeof(frame)); + + constexpr u16 AssociationIdMask = 0x3FFF; + return std::make_tuple(static_cast(frame.status_code), + frame.assoc_id & AssociationIdMask); +} + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_connection.h b/src/core/hle/service/nwm/uds_connection.h index 73f55a4fd..a664f8471 100644 --- a/src/core/hle/service/nwm/uds_connection.h +++ b/src/core/hle/service/nwm/uds_connection.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include "common/common_types.h" #include "common/swap.h" @@ -47,5 +48,9 @@ AuthenticationSeq GetAuthenticationSeqNumber(const std::vector& body); /// network id, starting at the frame body. std::vector GenerateAssocResponseFrame(AssocStatus status, u16 association_id, u32 network_id); +/// Returns a tuple of (association status, association id) from the body of an AssociationResponse +/// frame. +std::tuple GetAssociationResult(const std::vector& body); + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 8c6742dba..0fd9b8b8c 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -274,5 +274,26 @@ std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 return buffer; } +std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { + EAPoLStartPacket eapol_start{}; + eapol_start.association_id = association_id; + eapol_start.friend_code_seed = node_info.friend_code_seed; + + for (int i = 0; i < node_info.username.size(); ++i) + eapol_start.username[i] = node_info.username[i]; + + // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. + // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in + // EAPoL-Start packets from different 3DSs to the same host during a Super Smash Bros. 4 game. + // Find out what that means. + + std::vector eapol_buffer(sizeof(EAPoLStartPacket)); + std::memcpy(eapol_buffer.data(), &eapol_start, sizeof(eapol_start)); + + std::vector buffer = GenerateLLCHeader(EtherType::EAPoL); + buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.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 index a23520a41..76e8f546b 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h @@ -67,6 +67,27 @@ struct DataFrameCryptoCTR { static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); +constexpr u16 EAPoLStartMagic = 0x201; + +/* + * Nintendo EAPoLStartPacket, is used to initaliaze a connection between client and host + */ +struct EAPoLStartPacket { + u16_be magic = EAPoLStartMagic; + u16_be association_id; + // This value is hardcoded to 1 in the NWM module. + u16_be unknown = 1; + INSERT_PADDING_BYTES(2); + + u64_be friend_code_seed; + std::array username; + INSERT_PADDING_BYTES(4); + u16_be network_node_id; + INSERT_PADDING_BYTES(6); +}; + +static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); + /** * Generates an unencrypted 802.11 data payload. * @returns The generated frame payload. @@ -74,5 +95,12 @@ static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wron std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number); +/* + * Generates an unencrypted 802.11 data frame body with the EAPoL-Start format for UDS + * communication. + * @returns The generated frame body. + */ +std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); + } // namespace NWM } // namespace Service -- cgit v1.2.3 From c91ccbd0ba4118554d7377bbc3bd4c64f9bccf84 Mon Sep 17 00:00:00 2001 From: Max Thomas Date: Mon, 25 Sep 2017 00:17:38 -0600 Subject: Loader/NCCH: Add support for loading application updates (#2927) * loader/ncch: split NCCH parsing into its own file * loader/ncch: add support for loading update NCCHs from the SD card * loader/ncch: fix formatting * file_sys/ncch_container: Return a value for OpenFile * loader/ncch: cleanup, always instantiate overlay_ncch to base_ncch * file_sys/ncch_container: better encryption checks, allow non-app NCCHs to load properly and for the existence of NCCH structures to be checked * file_sys/ncch_container: pass filepath as a const reference --- src/core/CMakeLists.txt | 1 + src/core/file_sys/archive_selfncch.cpp | 28 ++- src/core/file_sys/archive_selfncch.h | 4 + src/core/file_sys/ncch_container.cpp | 316 ++++++++++++++++++++++++++++++++ src/core/file_sys/ncch_container.h | 244 +++++++++++++++++++++++++ src/core/loader/loader.h | 13 ++ src/core/loader/ncch.cpp | 319 +++++++-------------------------- src/core/loader/ncch.h | 184 +------------------ 8 files changed, 670 insertions(+), 439 deletions(-) create mode 100644 src/core/file_sys/ncch_container.cpp create mode 100644 src/core/file_sys/ncch_container.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index cd1a8de2d..3ed619991 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -26,6 +26,7 @@ set(SRCS file_sys/archive_systemsavedata.cpp file_sys/disk_archive.cpp file_sys/ivfc_archive.cpp + file_sys/ncch_container.cpp file_sys/path_parser.cpp file_sys/savedata_archive.cpp frontend/camera/blank_camera.cpp diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp index 298a37a44..7dc91a405 100644 --- a/src/core/file_sys/archive_selfncch.cpp +++ b/src/core/file_sys/archive_selfncch.cpp @@ -102,8 +102,7 @@ public: switch (static_cast(file_path.type)) { case SelfNCCHFilePathType::UpdateRomFS: - LOG_WARNING(Service_FS, "(STUBBED) open update RomFS"); - return OpenRomFS(); + return OpenUpdateRomFS(); case SelfNCCHFilePathType::RomFS: return OpenRomFS(); @@ -179,6 +178,17 @@ private: } } + ResultVal> OpenUpdateRomFS() const { + if (ncch_data.update_romfs_file) { + return MakeResult>(std::make_unique( + ncch_data.update_romfs_file, ncch_data.update_romfs_offset, + ncch_data.update_romfs_size)); + } else { + LOG_INFO(Service_FS, "Unable to read update RomFS"); + return ERROR_ROMFS_NOT_FOUND; + } + } + ResultVal> OpenExeFS(const std::string& filename) const { if (filename == "icon") { if (ncch_data.icon) { @@ -218,11 +228,19 @@ private: }; ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) { - std::shared_ptr romfs_file_; + std::shared_ptr romfs_file; + if (Loader::ResultStatus::Success == + app_loader.ReadRomFS(romfs_file, ncch_data.romfs_offset, ncch_data.romfs_size)) { + + ncch_data.romfs_file = std::move(romfs_file); + } + + std::shared_ptr update_romfs_file; if (Loader::ResultStatus::Success == - app_loader.ReadRomFS(romfs_file_, ncch_data.romfs_offset, ncch_data.romfs_size)) { + app_loader.ReadUpdateRomFS(update_romfs_file, ncch_data.update_romfs_offset, + ncch_data.update_romfs_size)) { - ncch_data.romfs_file = std::move(romfs_file_); + ncch_data.update_romfs_file = std::move(update_romfs_file); } std::vector buffer; diff --git a/src/core/file_sys/archive_selfncch.h b/src/core/file_sys/archive_selfncch.h index f1b971296..f1c659948 100644 --- a/src/core/file_sys/archive_selfncch.h +++ b/src/core/file_sys/archive_selfncch.h @@ -24,6 +24,10 @@ struct NCCHData { std::shared_ptr romfs_file; u64 romfs_offset = 0; u64 romfs_size = 0; + + std::shared_ptr update_romfs_file; + u64 update_romfs_offset = 0; + u64 update_romfs_size = 0; }; /// File system interface to the SelfNCCH archive diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp new file mode 100644 index 000000000..59c72f3e9 --- /dev/null +++ b/src/core/file_sys/ncch_container.cpp @@ -0,0 +1,316 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include +#include "common/common_types.h" +#include "common/logging/log.h" +#include "core/core.h" +#include "core/file_sys/ncch_container.h" +#include "core/loader/loader.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs +static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) + +/** + * Get the decompressed size of an LZSS compressed ExeFS file + * @param buffer Buffer of compressed file + * @param size Size of compressed buffer + * @return Size of decompressed buffer + */ +static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) { + u32 offset_size = *(u32*)(buffer + size - 4); + return offset_size + size; +} + +/** + * Decompress ExeFS file (compressed with LZSS) + * @param compressed Compressed buffer + * @param compressed_size Size of compressed buffer + * @param decompressed Decompressed buffer + * @param decompressed_size Size of decompressed buffer + * @return True on success, otherwise false + */ +static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed, + u32 decompressed_size) { + const u8* footer = compressed + compressed_size - 8; + u32 buffer_top_and_bottom = *reinterpret_cast(footer); + u32 out = decompressed_size; + u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); + u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); + + memset(decompressed, 0, decompressed_size); + memcpy(decompressed, compressed, compressed_size); + + while (index > stop_index) { + u8 control = compressed[--index]; + + for (unsigned i = 0; i < 8; i++) { + if (index <= stop_index) + break; + if (index <= 0) + break; + if (out <= 0) + break; + + if (control & 0x80) { + // Check if compression is out of bounds + if (index < 2) + return false; + index -= 2; + + u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); + u32 segment_size = ((segment_offset >> 12) & 15) + 3; + segment_offset &= 0x0FFF; + segment_offset += 2; + + // Check if compression is out of bounds + if (out < segment_size) + return false; + + for (unsigned j = 0; j < segment_size; j++) { + // Check if compression is out of bounds + if (out + segment_offset >= decompressed_size) + return false; + + u8 data = decompressed[out + segment_offset]; + decompressed[--out] = data; + } + } else { + // Check if compression is out of bounds + if (out < 1) + return false; + decompressed[--out] = compressed[--index]; + } + control <<= 1; + } + } + return true; +} + +NCCHContainer::NCCHContainer(const std::string& filepath) : filepath(filepath) { + file = FileUtil::IOFile(filepath, "rb"); +} + +Loader::ResultStatus NCCHContainer::OpenFile(const std::string& filepath) { + this->filepath = filepath; + file = FileUtil::IOFile(filepath, "rb"); + + if (!file.IsOpen()) { + LOG_WARNING(Service_FS, "Failed to open %s", filepath.c_str()); + return Loader::ResultStatus::Error; + } + + LOG_DEBUG(Service_FS, "Opened %s", filepath.c_str()); + return Loader::ResultStatus::Success; +} + +Loader::ResultStatus NCCHContainer::Load() { + if (is_loaded) + return Loader::ResultStatus::Success; + + // Reset read pointer in case this file has been read before. + file.Seek(0, SEEK_SET); + + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + return Loader::ResultStatus::Error; + + // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... + if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { + LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!"); + ncch_offset = 0x4000; + file.Seek(ncch_offset, SEEK_SET); + file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); + } + + // Verify we are loading the correct file type... + if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) + return Loader::ResultStatus::ErrorInvalidFormat; + + // System archives and DLC don't have an extended header but have RomFS + if (ncch_header.extended_header_size) { + if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) + return Loader::ResultStatus::Error; + + is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; + u32 entry_point = exheader_header.codeset_info.text.address; + u32 code_size = exheader_header.codeset_info.text.code_size; + u32 stack_size = exheader_header.codeset_info.stack_size; + u32 bss_size = exheader_header.codeset_info.bss_size; + u32 core_version = exheader_header.arm11_system_local_caps.core_version; + u8 priority = exheader_header.arm11_system_local_caps.priority; + u8 resource_limit_category = + exheader_header.arm11_system_local_caps.resource_limit_category; + + LOG_DEBUG(Service_FS, "Name: %s", exheader_header.codeset_info.name); + LOG_DEBUG(Service_FS, "Program ID: %016" PRIX64, ncch_header.program_id); + LOG_DEBUG(Service_FS, "Code compressed: %s", is_compressed ? "yes" : "no"); + LOG_DEBUG(Service_FS, "Entry point: 0x%08X", entry_point); + LOG_DEBUG(Service_FS, "Code size: 0x%08X", code_size); + LOG_DEBUG(Service_FS, "Stack size: 0x%08X", stack_size); + LOG_DEBUG(Service_FS, "Bss size: 0x%08X", bss_size); + LOG_DEBUG(Service_FS, "Core version: %d", core_version); + LOG_DEBUG(Service_FS, "Thread priority: 0x%X", priority); + LOG_DEBUG(Service_FS, "Resource limit category: %d", resource_limit_category); + LOG_DEBUG(Service_FS, "System Mode: %d", + static_cast(exheader_header.arm11_system_local_caps.system_mode)); + + if (exheader_header.system_info.jump_id != ncch_header.program_id) { + LOG_ERROR(Service_FS, "ExHeader Program ID mismatch: the ROM is probably encrypted."); + return Loader::ResultStatus::ErrorEncrypted; + } + + has_exheader = true; + } + + // DLC can have an ExeFS and a RomFS but no extended header + if (ncch_header.exefs_size) { + exefs_offset = ncch_header.exefs_offset * kBlockSize; + u32 exefs_size = ncch_header.exefs_size * kBlockSize; + + LOG_DEBUG(Service_FS, "ExeFS offset: 0x%08X", exefs_offset); + LOG_DEBUG(Service_FS, "ExeFS size: 0x%08X", exefs_size); + + file.Seek(exefs_offset + ncch_offset, SEEK_SET); + if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) + return Loader::ResultStatus::Error; + + has_exefs = true; + } + + if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) + has_romfs = true; + + is_loaded = true; + return Loader::ResultStatus::Success; +} + +Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vector& buffer) { + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + Loader::ResultStatus result = Load(); + if (result != Loader::ResultStatus::Success) + return result; + + if (!has_exefs) + return Loader::ResultStatus::ErrorNotUsed; + + LOG_DEBUG(Service_FS, "%d sections:", kMaxSections); + // Iterate through the ExeFs archive until we find a section with the specified name... + for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { + const auto& section = exefs_header.section[section_number]; + + // Load the specified section... + if (strcmp(section.name, name) == 0) { + LOG_DEBUG(Service_FS, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number, + section.offset, section.size, section.name); + + s64 section_offset = + (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); + file.Seek(section_offset, SEEK_SET); + + if (strcmp(section.name, ".code") == 0 && is_compressed) { + // Section is compressed, read compressed .code section... + std::unique_ptr temp_buffer; + try { + temp_buffer.reset(new u8[section.size]); + } catch (std::bad_alloc&) { + return Loader::ResultStatus::ErrorMemoryAllocationFailed; + } + + if (file.ReadBytes(&temp_buffer[0], section.size) != section.size) + return Loader::ResultStatus::Error; + + // Decompress .code section... + u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size); + buffer.resize(decompressed_size); + if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size)) + return Loader::ResultStatus::ErrorInvalidFormat; + } else { + // Section is uncompressed... + buffer.resize(section.size); + if (file.ReadBytes(&buffer[0], section.size) != section.size) + return Loader::ResultStatus::Error; + } + return Loader::ResultStatus::Success; + } + } + return Loader::ResultStatus::ErrorNotUsed; +} + +Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romfs_file, + u64& offset, u64& size) { + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + Loader::ResultStatus result = Load(); + if (result != Loader::ResultStatus::Success) + return result; + + if (!has_romfs) { + LOG_DEBUG(Service_FS, "RomFS requested from NCCH which has no RomFS"); + return Loader::ResultStatus::ErrorNotUsed; + } + + u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; + u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; + + LOG_DEBUG(Service_FS, "RomFS offset: 0x%08X", romfs_offset); + LOG_DEBUG(Service_FS, "RomFS size: 0x%08X", romfs_size); + + if (file.GetSize() < romfs_offset + romfs_size) + return Loader::ResultStatus::Error; + + // We reopen the file, to allow its position to be independent from file's + romfs_file = std::make_shared(filepath, "rb"); + if (!romfs_file->IsOpen()) + return Loader::ResultStatus::Error; + + offset = romfs_offset; + size = romfs_size; + + return Loader::ResultStatus::Success; +} + +Loader::ResultStatus NCCHContainer::ReadProgramId(u64_le& program_id) { + Loader::ResultStatus result = Load(); + if (result != Loader::ResultStatus::Success) + return result; + + program_id = ncch_header.program_id; + return Loader::ResultStatus::Success; +} + +bool NCCHContainer::HasExeFS() { + Loader::ResultStatus result = Load(); + if (result != Loader::ResultStatus::Success) + return false; + + return has_exefs; +} + +bool NCCHContainer::HasRomFS() { + Loader::ResultStatus result = Load(); + if (result != Loader::ResultStatus::Success) + return false; + + return has_romfs; +} + +bool NCCHContainer::HasExHeader() { + Loader::ResultStatus result = Load(); + if (result != Loader::ResultStatus::Success) + return false; + + return has_exheader; +} + +} // namespace FileSys diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h new file mode 100644 index 000000000..8af9032b4 --- /dev/null +++ b/src/core/file_sys/ncch_container.h @@ -0,0 +1,244 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include +#include "common/bit_field.h" +#include "common/common_types.h" +#include "common/file_util.h" +#include "common/swap.h" +#include "core/core.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym) + +struct NCCH_Header { + u8 signature[0x100]; + u32_le magic; + u32_le content_size; + u8 partition_id[8]; + u16_le maker_code; + u16_le version; + u8 reserved_0[4]; + u64_le program_id; + u8 reserved_1[0x10]; + u8 logo_region_hash[0x20]; + u8 product_code[0x10]; + u8 extended_header_hash[0x20]; + u32_le extended_header_size; + u8 reserved_2[4]; + u8 flags[8]; + u32_le plain_region_offset; + u32_le plain_region_size; + u32_le logo_region_offset; + u32_le logo_region_size; + u32_le exefs_offset; + u32_le exefs_size; + u32_le exefs_hash_region_size; + u8 reserved_3[4]; + u32_le romfs_offset; + u32_le romfs_size; + u32_le romfs_hash_region_size; + u8 reserved_4[4]; + u8 exefs_super_block_hash[0x20]; + u8 romfs_super_block_hash[0x20]; +}; + +static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong"); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ExeFS (executable file system) headers + +struct ExeFs_SectionHeader { + char name[8]; + u32 offset; + u32 size; +}; + +struct ExeFs_Header { + ExeFs_SectionHeader section[8]; + u8 reserved[0x80]; + u8 hashes[8][0x20]; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ExHeader (executable file system header) headers + +struct ExHeader_SystemInfoFlags { + u8 reserved[5]; + u8 flag; + u8 remaster_version[2]; +}; + +struct ExHeader_CodeSegmentInfo { + u32 address; + u32 num_max_pages; + u32 code_size; +}; + +struct ExHeader_CodeSetInfo { + u8 name[8]; + ExHeader_SystemInfoFlags flags; + ExHeader_CodeSegmentInfo text; + u32 stack_size; + ExHeader_CodeSegmentInfo ro; + u8 reserved[4]; + ExHeader_CodeSegmentInfo data; + u32 bss_size; +}; + +struct ExHeader_DependencyList { + u8 program_id[0x30][8]; +}; + +struct ExHeader_SystemInfo { + u64 save_data_size; + u64_le jump_id; + u8 reserved_2[0x30]; +}; + +struct ExHeader_StorageInfo { + u8 ext_save_data_id[8]; + u8 system_save_data_id[8]; + u8 reserved[8]; + u8 access_info[7]; + u8 other_attributes; +}; + +struct ExHeader_ARM11_SystemLocalCaps { + u64_le program_id; + u32_le core_version; + u8 reserved_flags[2]; + union { + u8 flags0; + BitField<0, 2, u8> ideal_processor; + BitField<2, 2, u8> affinity_mask; + BitField<4, 4, u8> system_mode; + }; + u8 priority; + u8 resource_limit_descriptor[0x10][2]; + ExHeader_StorageInfo storage_info; + u8 service_access_control[0x20][8]; + u8 ex_service_access_control[0x2][8]; + u8 reserved[0xf]; + u8 resource_limit_category; +}; + +struct ExHeader_ARM11_KernelCaps { + u32_le descriptors[28]; + u8 reserved[0x10]; +}; + +struct ExHeader_ARM9_AccessControl { + u8 descriptors[15]; + u8 descversion; +}; + +struct ExHeader_Header { + ExHeader_CodeSetInfo codeset_info; + ExHeader_DependencyList dependency_list; + ExHeader_SystemInfo system_info; + ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; + ExHeader_ARM11_KernelCaps arm11_kernel_caps; + ExHeader_ARM9_AccessControl arm9_access_control; + struct { + u8 signature[0x100]; + u8 ncch_public_key_modulus[0x100]; + ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; + ExHeader_ARM11_KernelCaps arm11_kernel_caps; + ExHeader_ARM9_AccessControl arm9_access_control; + } access_desc; +}; + +static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong"); + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +/** + * Helper which implements an interface to deal with NCCH containers which can + * contain ExeFS archives or RomFS archives for games or other applications. + */ +class NCCHContainer { +public: + NCCHContainer(const std::string& filepath); + NCCHContainer() {} + + Loader::ResultStatus OpenFile(const std::string& filepath); + + /** + * Ensure ExeFS and exheader is loaded and ready for reading sections + * @return ResultStatus result of function + */ + Loader::ResultStatus Load(); + + /** + * Reads an application ExeFS section of an NCCH file (e.g. .code, .logo, etc.) + * @param name Name of section to read out of NCCH file + * @param buffer Vector to read data into + * @return ResultStatus result of function + */ + Loader::ResultStatus LoadSectionExeFS(const char* name, std::vector& buffer); + + /** + * Get the RomFS of the NCCH container + * Since the RomFS can be huge, we return a file reference instead of copying to a buffer + * @param romfs_file The file containing the RomFS + * @param offset The offset the romfs begins on + * @param size The size of the romfs + * @return ResultStatus result of function + */ + Loader::ResultStatus ReadRomFS(std::shared_ptr& romfs_file, u64& offset, + u64& size); + + /** + * Get the Program ID of the NCCH container + * @return ResultStatus result of function + */ + Loader::ResultStatus ReadProgramId(u64_le& program_id); + + /** + * Checks whether the NCCH container contains an ExeFS + * @return bool check result + */ + bool HasExeFS(); + + /** + * Checks whether the NCCH container contains a RomFS + * @return bool check result + */ + bool HasRomFS(); + + /** + * Checks whether the NCCH container contains an ExHeader + * @return bool check result + */ + bool HasExHeader(); + + NCCH_Header ncch_header; + ExeFs_Header exefs_header; + ExHeader_Header exheader_header; + +private: + bool has_exheader = false; + bool has_exefs = false; + bool has_romfs = false; + + bool is_loaded = false; + bool is_compressed = false; + + u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header + u32 exefs_offset = 0; + + std::string filepath; + FileUtil::IOFile file; +}; + +} // namespace FileSys diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index e731888a2..3160fd2fd 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -166,6 +166,19 @@ public: return ResultStatus::ErrorNotImplemented; } + /** + * Get the update RomFS of the application + * Since the RomFS can be huge, we return a file reference instead of copying to a buffer + * @param romfs_file The file containing the RomFS + * @param offset The offset the romfs begins on + * @param size The size of the romfs + * @return ResultStatus result of function + */ + virtual ResultStatus ReadUpdateRomFS(std::shared_ptr& romfs_file, u64& offset, + u64& size) { + return ResultStatus::ErrorNotImplemented; + } + /** * Get the title of the application * @param title Reference to store the application title into diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 79ea50147..bef7fa567 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -13,6 +13,7 @@ #include "common/swap.h" #include "core/core.h" #include "core/file_sys/archive_selfncch.h" +#include "core/file_sys/ncch_container.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/service/cfg/cfg.h" @@ -27,87 +28,7 @@ namespace Loader { -static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs -static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) - -/** - * Get the decompressed size of an LZSS compressed ExeFS file - * @param buffer Buffer of compressed file - * @param size Size of compressed buffer - * @return Size of decompressed buffer - */ -static u32 LZSS_GetDecompressedSize(const u8* buffer, u32 size) { - u32 offset_size = *(u32*)(buffer + size - 4); - return offset_size + size; -} - -/** - * Decompress ExeFS file (compressed with LZSS) - * @param compressed Compressed buffer - * @param compressed_size Size of compressed buffer - * @param decompressed Decompressed buffer - * @param decompressed_size Size of decompressed buffer - * @return True on success, otherwise false - */ -static bool LZSS_Decompress(const u8* compressed, u32 compressed_size, u8* decompressed, - u32 decompressed_size) { - const u8* footer = compressed + compressed_size - 8; - u32 buffer_top_and_bottom = *reinterpret_cast(footer); - u32 out = decompressed_size; - u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); - u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); - - memset(decompressed, 0, decompressed_size); - memcpy(decompressed, compressed, compressed_size); - - while (index > stop_index) { - u8 control = compressed[--index]; - - for (unsigned i = 0; i < 8; i++) { - if (index <= stop_index) - break; - if (index <= 0) - break; - if (out <= 0) - break; - - if (control & 0x80) { - // Check if compression is out of bounds - if (index < 2) - return false; - index -= 2; - - u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); - u32 segment_size = ((segment_offset >> 12) & 15) + 3; - segment_offset &= 0x0FFF; - segment_offset += 2; - - // Check if compression is out of bounds - if (out < segment_size) - return false; - - for (unsigned j = 0; j < segment_size; j++) { - // Check if compression is out of bounds - if (out + segment_offset >= decompressed_size) - return false; - - u8 data = decompressed[out + segment_offset]; - decompressed[--out] = data; - } - } else { - // Check if compression is out of bounds - if (out < 1) - return false; - decompressed[--out] = compressed[--index]; - } - control <<= 1; - } - } - return true; -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// AppLoader_NCCH class +static const u64 UPDATE_MASK = 0x0000000e00000000; FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { u32 magic; @@ -124,15 +45,25 @@ FileType AppLoader_NCCH::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } +static std::string GetUpdateNCCHPath(u64_le program_id) { + u32 high = static_cast((program_id | UPDATE_MASK) >> 32); + u32 low = static_cast((program_id | UPDATE_MASK) & 0xFFFFFFFF); + + return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/%08x/%08x/content/00000000.app", + FileUtil::GetUserPath(D_SDMC_IDX).c_str(), SYSTEM_ID, SDCARD_ID, + high, low); +} + std::pair, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { if (!is_loaded) { - ResultStatus res = LoadExeFS(); + ResultStatus res = base_ncch.Load(); if (res != ResultStatus::Success) { return std::make_pair(boost::none, res); } } + // Set the system mode as the one from the exheader. - return std::make_pair(exheader_header.arm11_system_local_caps.system_mode.Value(), + return std::make_pair(overlay_ncch->exheader_header.arm11_system_local_caps.system_mode.Value(), ResultStatus::Success); } @@ -144,29 +75,34 @@ ResultStatus AppLoader_NCCH::LoadExec() { return ResultStatus::ErrorNotLoaded; std::vector code; - if (ResultStatus::Success == ReadCode(code)) { + u64_le program_id; + if (ResultStatus::Success == ReadCode(code) && + ResultStatus::Success == ReadProgramId(program_id)) { std::string process_name = Common::StringFromFixedZeroTerminatedBuffer( - (const char*)exheader_header.codeset_info.name, 8); + (const char*)overlay_ncch->exheader_header.codeset_info.name, 8); - SharedPtr codeset = CodeSet::Create(process_name, ncch_header.program_id); + SharedPtr codeset = CodeSet::Create(process_name, program_id); codeset->code.offset = 0; - codeset->code.addr = exheader_header.codeset_info.text.address; - codeset->code.size = exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; + codeset->code.addr = overlay_ncch->exheader_header.codeset_info.text.address; + codeset->code.size = + overlay_ncch->exheader_header.codeset_info.text.num_max_pages * Memory::PAGE_SIZE; codeset->rodata.offset = codeset->code.offset + codeset->code.size; - codeset->rodata.addr = exheader_header.codeset_info.ro.address; - codeset->rodata.size = exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; + codeset->rodata.addr = overlay_ncch->exheader_header.codeset_info.ro.address; + codeset->rodata.size = + overlay_ncch->exheader_header.codeset_info.ro.num_max_pages * Memory::PAGE_SIZE; // TODO(yuriks): Not sure if the bss size is added to the page-aligned .data size or just // to the regular size. Playing it safe for now. - u32 bss_page_size = (exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; + u32 bss_page_size = (overlay_ncch->exheader_header.codeset_info.bss_size + 0xFFF) & ~0xFFF; code.resize(code.size() + bss_page_size, 0); codeset->data.offset = codeset->rodata.offset + codeset->rodata.size; - codeset->data.addr = exheader_header.codeset_info.data.address; + codeset->data.addr = overlay_ncch->exheader_header.codeset_info.data.address; codeset->data.size = - exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + bss_page_size; + overlay_ncch->exheader_header.codeset_info.data.num_max_pages * Memory::PAGE_SIZE + + bss_page_size; codeset->entrypoint = codeset->code.addr; codeset->memory = std::make_shared>(std::move(code)); @@ -177,150 +113,27 @@ ResultStatus AppLoader_NCCH::LoadExec() { // Attach a resource limit to the process based on the resource limit category Kernel::g_current_process->resource_limit = Kernel::ResourceLimit::GetForCategory(static_cast( - exheader_header.arm11_system_local_caps.resource_limit_category)); + overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category)); // Set the default CPU core for this process Kernel::g_current_process->ideal_processor = - exheader_header.arm11_system_local_caps.ideal_processor; + overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor; // Copy data while converting endianness - std::array kernel_caps; - std::copy_n(exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), + std::arrayexheader_header.arm11_kernel_caps.descriptors)> + kernel_caps; + std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); - s32 priority = exheader_header.arm11_system_local_caps.priority; - u32 stack_size = exheader_header.codeset_info.stack_size; + s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority; + u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size; Kernel::g_current_process->Run(priority, stack_size); return ResultStatus::Success; } return ResultStatus::Error; } -ResultStatus AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector& buffer) { - if (!file.IsOpen()) - return ResultStatus::Error; - - ResultStatus result = LoadExeFS(); - if (result != ResultStatus::Success) - return result; - - LOG_DEBUG(Loader, "%d sections:", kMaxSections); - // Iterate through the ExeFs archive until we find a section with the specified name... - for (unsigned section_number = 0; section_number < kMaxSections; section_number++) { - const auto& section = exefs_header.section[section_number]; - - // Load the specified section... - if (strcmp(section.name, name) == 0) { - LOG_DEBUG(Loader, "%d - offset: 0x%08X, size: 0x%08X, name: %s", section_number, - section.offset, section.size, section.name); - - s64 section_offset = - (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); - file.Seek(section_offset, SEEK_SET); - - if (strcmp(section.name, ".code") == 0 && is_compressed) { - // Section is compressed, read compressed .code section... - std::unique_ptr temp_buffer; - try { - temp_buffer.reset(new u8[section.size]); - } catch (std::bad_alloc&) { - return ResultStatus::ErrorMemoryAllocationFailed; - } - - if (file.ReadBytes(&temp_buffer[0], section.size) != section.size) - return ResultStatus::Error; - - // Decompress .code section... - u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], section.size); - buffer.resize(decompressed_size); - if (!LZSS_Decompress(&temp_buffer[0], section.size, &buffer[0], decompressed_size)) - return ResultStatus::ErrorInvalidFormat; - } else { - // Section is uncompressed... - buffer.resize(section.size); - if (file.ReadBytes(&buffer[0], section.size) != section.size) - return ResultStatus::Error; - } - return ResultStatus::Success; - } - } - return ResultStatus::ErrorNotUsed; -} - -ResultStatus AppLoader_NCCH::LoadExeFS() { - if (is_exefs_loaded) - return ResultStatus::Success; - - if (!file.IsOpen()) - return ResultStatus::Error; - - // Reset read pointer in case this file has been read before. - file.Seek(0, SEEK_SET); - - if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) - return ResultStatus::Error; - - // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... - if (MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { - LOG_DEBUG(Loader, "Only loading the first (bootable) NCCH within the NCSD file!"); - ncch_offset = 0x4000; - file.Seek(ncch_offset, SEEK_SET); - file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); - } - - // Verify we are loading the correct file type... - if (MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) - return ResultStatus::ErrorInvalidFormat; - - // Read ExHeader... - - if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) - return ResultStatus::Error; - - is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; - entry_point = exheader_header.codeset_info.text.address; - code_size = exheader_header.codeset_info.text.code_size; - stack_size = exheader_header.codeset_info.stack_size; - bss_size = exheader_header.codeset_info.bss_size; - core_version = exheader_header.arm11_system_local_caps.core_version; - priority = exheader_header.arm11_system_local_caps.priority; - resource_limit_category = exheader_header.arm11_system_local_caps.resource_limit_category; - - LOG_DEBUG(Loader, "Name: %s", exheader_header.codeset_info.name); - LOG_DEBUG(Loader, "Program ID: %016" PRIX64, ncch_header.program_id); - LOG_DEBUG(Loader, "Code compressed: %s", is_compressed ? "yes" : "no"); - LOG_DEBUG(Loader, "Entry point: 0x%08X", entry_point); - LOG_DEBUG(Loader, "Code size: 0x%08X", code_size); - LOG_DEBUG(Loader, "Stack size: 0x%08X", stack_size); - LOG_DEBUG(Loader, "Bss size: 0x%08X", bss_size); - LOG_DEBUG(Loader, "Core version: %d", core_version); - LOG_DEBUG(Loader, "Thread priority: 0x%X", priority); - LOG_DEBUG(Loader, "Resource limit category: %d", resource_limit_category); - LOG_DEBUG(Loader, "System Mode: %d", - static_cast(exheader_header.arm11_system_local_caps.system_mode)); - - if (exheader_header.arm11_system_local_caps.program_id != ncch_header.program_id) { - LOG_ERROR(Loader, "ExHeader Program ID mismatch: the ROM is probably encrypted."); - return ResultStatus::ErrorEncrypted; - } - - // Read ExeFS... - - exefs_offset = ncch_header.exefs_offset * kBlockSize; - u32 exefs_size = ncch_header.exefs_size * kBlockSize; - - LOG_DEBUG(Loader, "ExeFS offset: 0x%08X", exefs_offset); - LOG_DEBUG(Loader, "ExeFS size: 0x%08X", exefs_size); - - file.Seek(exefs_offset + ncch_offset, SEEK_SET); - if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) - return ResultStatus::Error; - - is_exefs_loaded = true; - return ResultStatus::Success; -} - void AppLoader_NCCH::ParseRegionLockoutInfo() { std::vector smdh_buffer; if (ReadIcon(smdh_buffer) == ResultStatus::Success && smdh_buffer.size() >= sizeof(SMDH)) { @@ -339,23 +152,32 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() { } ResultStatus AppLoader_NCCH::Load() { + u64_le ncch_program_id; + if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - ResultStatus result = LoadExeFS(); + ResultStatus result = base_ncch.Load(); if (result != ResultStatus::Success) return result; - std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_header.program_id)}; + ReadProgramId(ncch_program_id); + std::string program_id{Common::StringFromFormat("%016" PRIX64, ncch_program_id)}; LOG_INFO(Loader, "Program ID: %s", program_id.c_str()); + update_ncch.OpenFile(GetUpdateNCCHPath(ncch_program_id)); + result = update_ncch.Load(); + if (result == ResultStatus::Success) { + overlay_ncch = &update_ncch; + } + Core::Telemetry().AddField(Telemetry::FieldType::Session, "ProgramId", program_id); if (auto room_member = Network::GetRoomMember().lock()) { Network::GameInfo game_info; ReadTitle(game_info.name); - game_info.id = ncch_header.program_id; + game_info.id = ncch_program_id; room_member->SendGameInfo(game_info); } @@ -374,61 +196,40 @@ ResultStatus AppLoader_NCCH::Load() { } ResultStatus AppLoader_NCCH::ReadCode(std::vector& buffer) { - return LoadSectionExeFS(".code", buffer); + return overlay_ncch->LoadSectionExeFS(".code", buffer); } ResultStatus AppLoader_NCCH::ReadIcon(std::vector& buffer) { - return LoadSectionExeFS("icon", buffer); + return overlay_ncch->LoadSectionExeFS("icon", buffer); } ResultStatus AppLoader_NCCH::ReadBanner(std::vector& buffer) { - return LoadSectionExeFS("banner", buffer); + return overlay_ncch->LoadSectionExeFS("banner", buffer); } ResultStatus AppLoader_NCCH::ReadLogo(std::vector& buffer) { - return LoadSectionExeFS("logo", buffer); + return overlay_ncch->LoadSectionExeFS("logo", buffer); } ResultStatus AppLoader_NCCH::ReadProgramId(u64& out_program_id) { - if (!file.IsOpen()) - return ResultStatus::Error; - - ResultStatus result = LoadExeFS(); + ResultStatus result = base_ncch.ReadProgramId(out_program_id); if (result != ResultStatus::Success) return result; - out_program_id = ncch_header.program_id; return ResultStatus::Success; } ResultStatus AppLoader_NCCH::ReadRomFS(std::shared_ptr& romfs_file, u64& offset, u64& size) { - if (!file.IsOpen()) - return ResultStatus::Error; - - // Check if the NCCH has a RomFS... - if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { - u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; - u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; - - LOG_DEBUG(Loader, "RomFS offset: 0x%08X", romfs_offset); - LOG_DEBUG(Loader, "RomFS size: 0x%08X", romfs_size); - - if (file.GetSize() < romfs_offset + romfs_size) - return ResultStatus::Error; - - // We reopen the file, to allow its position to be independent from file's - romfs_file = std::make_shared(filepath, "rb"); - if (!romfs_file->IsOpen()) - return ResultStatus::Error; + return base_ncch.ReadRomFS(romfs_file, offset, size); +} - offset = romfs_offset; - size = romfs_size; +ResultStatus AppLoader_NCCH::ReadUpdateRomFS(std::shared_ptr& romfs_file, + u64& offset, u64& size) { + ResultStatus result = update_ncch.ReadRomFS(romfs_file, offset, size); - return ResultStatus::Success; - } - LOG_DEBUG(Loader, "NCCH has no RomFS"); - return ResultStatus::ErrorNotUsed; + if (result != ResultStatus::Success) + return base_ncch.ReadRomFS(romfs_file, offset, size); } ResultStatus AppLoader_NCCH::ReadTitle(std::string& title) { diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index e40cef764..9b56465cb 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -5,154 +5,11 @@ #pragma once #include -#include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/file_sys/ncch_container.h" #include "core/loader/loader.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// NCCH header (Note: "NCCH" appears to be a publicly unknown acronym) - -struct NCCH_Header { - u8 signature[0x100]; - u32_le magic; - u32_le content_size; - u8 partition_id[8]; - u16_le maker_code; - u16_le version; - u8 reserved_0[4]; - u64_le program_id; - u8 reserved_1[0x10]; - u8 logo_region_hash[0x20]; - u8 product_code[0x10]; - u8 extended_header_hash[0x20]; - u32_le extended_header_size; - u8 reserved_2[4]; - u8 flags[8]; - u32_le plain_region_offset; - u32_le plain_region_size; - u32_le logo_region_offset; - u32_le logo_region_size; - u32_le exefs_offset; - u32_le exefs_size; - u32_le exefs_hash_region_size; - u8 reserved_3[4]; - u32_le romfs_offset; - u32_le romfs_size; - u32_le romfs_hash_region_size; - u8 reserved_4[4]; - u8 exefs_super_block_hash[0x20]; - u8 romfs_super_block_hash[0x20]; -}; - -static_assert(sizeof(NCCH_Header) == 0x200, "NCCH header structure size is wrong"); - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ExeFS (executable file system) headers - -struct ExeFs_SectionHeader { - char name[8]; - u32 offset; - u32 size; -}; - -struct ExeFs_Header { - ExeFs_SectionHeader section[8]; - u8 reserved[0x80]; - u8 hashes[8][0x20]; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ExHeader (executable file system header) headers - -struct ExHeader_SystemInfoFlags { - u8 reserved[5]; - u8 flag; - u8 remaster_version[2]; -}; - -struct ExHeader_CodeSegmentInfo { - u32 address; - u32 num_max_pages; - u32 code_size; -}; - -struct ExHeader_CodeSetInfo { - u8 name[8]; - ExHeader_SystemInfoFlags flags; - ExHeader_CodeSegmentInfo text; - u32 stack_size; - ExHeader_CodeSegmentInfo ro; - u8 reserved[4]; - ExHeader_CodeSegmentInfo data; - u32 bss_size; -}; - -struct ExHeader_DependencyList { - u8 program_id[0x30][8]; -}; - -struct ExHeader_SystemInfo { - u64 save_data_size; - u8 jump_id[8]; - u8 reserved_2[0x30]; -}; - -struct ExHeader_StorageInfo { - u8 ext_save_data_id[8]; - u8 system_save_data_id[8]; - u8 reserved[8]; - u8 access_info[7]; - u8 other_attributes; -}; - -struct ExHeader_ARM11_SystemLocalCaps { - u64_le program_id; - u32_le core_version; - u8 reserved_flags[2]; - union { - u8 flags0; - BitField<0, 2, u8> ideal_processor; - BitField<2, 2, u8> affinity_mask; - BitField<4, 4, u8> system_mode; - }; - u8 priority; - u8 resource_limit_descriptor[0x10][2]; - ExHeader_StorageInfo storage_info; - u8 service_access_control[0x20][8]; - u8 ex_service_access_control[0x2][8]; - u8 reserved[0xf]; - u8 resource_limit_category; -}; - -struct ExHeader_ARM11_KernelCaps { - u32_le descriptors[28]; - u8 reserved[0x10]; -}; - -struct ExHeader_ARM9_AccessControl { - u8 descriptors[15]; - u8 descversion; -}; - -struct ExHeader_Header { - ExHeader_CodeSetInfo codeset_info; - ExHeader_DependencyList dependency_list; - ExHeader_SystemInfo system_info; - ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; - ExHeader_ARM11_KernelCaps arm11_kernel_caps; - ExHeader_ARM9_AccessControl arm9_access_control; - struct { - u8 signature[0x100]; - u8 ncch_public_key_modulus[0x100]; - ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; - ExHeader_ARM11_KernelCaps arm11_kernel_caps; - ExHeader_ARM9_AccessControl arm9_access_control; - } access_desc; -}; - -static_assert(sizeof(ExHeader_Header) == 0x800, "ExHeader structure size is wrong"); - //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -162,7 +19,8 @@ namespace Loader { class AppLoader_NCCH final : public AppLoader { public: AppLoader_NCCH(FileUtil::IOFile&& file, const std::string& filepath) - : AppLoader(std::move(file)), filepath(filepath) {} + : AppLoader(std::move(file)), filepath(filepath), base_ncch(filepath), + overlay_ncch(&base_ncch) {} /** * Returns the type of the file @@ -196,48 +54,24 @@ public: ResultStatus ReadRomFS(std::shared_ptr& romfs_file, u64& offset, u64& size) override; + ResultStatus ReadUpdateRomFS(std::shared_ptr& romfs_file, u64& offset, + u64& size) override; + ResultStatus ReadTitle(std::string& title) override; private: - /** - * Reads an application ExeFS section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) - * @param name Name of section to read out of NCCH file - * @param buffer Vector to read data into - * @return ResultStatus result of function - */ - ResultStatus LoadSectionExeFS(const char* name, std::vector& buffer); - /** * Loads .code section into memory for booting * @return ResultStatus result of function */ ResultStatus LoadExec(); - /** - * Ensure ExeFS is loaded and ready for reading sections - * @return ResultStatus result of function - */ - ResultStatus LoadExeFS(); - /// Reads the region lockout info in the SMDH and send it to CFG service void ParseRegionLockoutInfo(); - bool is_exefs_loaded = false; - bool is_compressed = false; - - u32 entry_point = 0; - u32 code_size = 0; - u32 stack_size = 0; - u32 bss_size = 0; - u32 core_version = 0; - u8 priority = 0; - u8 resource_limit_category = 0; - u32 ncch_offset = 0; // Offset to NCCH header, can be 0 or after NCSD header - u32 exefs_offset = 0; - - NCCH_Header ncch_header; - ExeFs_Header exefs_header; - ExHeader_Header exheader_header; + FileSys::NCCHContainer base_ncch; + FileSys::NCCHContainer update_ncch; + FileSys::NCCHContainer* overlay_ncch; std::string filepath; }; -- cgit v1.2.3 From 774e7deae8655a6f09530770c56ae2e75d55309b Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 23 Sep 2017 20:32:18 -0500 Subject: HLE/Archives: Allow multiple loaded applications to access their SelfNCCH archive independently. The loaders now register each loaded ROM with the SelfNCCH factory, which keeps the data around for the duration of the emulation session. When opening the SelfNCCH archive, the factory queries the current program's programid and uses that as a key to the map that contains the NCCHData structure (RomFS, Icon, Banner, etc). 3dsx files do not have a programid and will use a default of 0 for this value, thus, only 1 3dsx file with RomFS is loadable at the same time. --- src/core/file_sys/archive_selfncch.cpp | 43 +++++++++++++++++++++++++--------- src/core/file_sys/archive_selfncch.h | 9 +++++-- src/core/hle/service/fs/archive.cpp | 18 +++++++++++++- src/core/hle/service/fs/archive.h | 7 ++++++ src/core/loader/3dsx.cpp | 3 +-- src/core/loader/ncch.cpp | 3 +-- 6 files changed, 65 insertions(+), 18 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/archive_selfncch.cpp b/src/core/file_sys/archive_selfncch.cpp index 7dc91a405..a16941c70 100644 --- a/src/core/file_sys/archive_selfncch.cpp +++ b/src/core/file_sys/archive_selfncch.cpp @@ -3,12 +3,14 @@ // Refer to the license.txt file included. #include +#include #include "common/common_types.h" #include "common/logging/log.h" #include "common/swap.h" #include "core/file_sys/archive_selfncch.h" #include "core/file_sys/errors.h" #include "core/file_sys/ivfc_archive.h" +#include "core/hle/kernel/process.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -227,38 +229,57 @@ private: NCCHData ncch_data; }; -ArchiveFactory_SelfNCCH::ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader) { - std::shared_ptr romfs_file; +void ArchiveFactory_SelfNCCH::Register(Loader::AppLoader& app_loader) { + u64 program_id = 0; + if (app_loader.ReadProgramId(program_id) != Loader::ResultStatus::Success) { + LOG_WARNING( + Service_FS, + "Could not read program id when registering with SelfNCCH, this might be a 3dsx file"); + } + + LOG_DEBUG(Service_FS, "Registering program %016" PRIX64 " with the SelfNCCH archive factory", + program_id); + + if (ncch_data.find(program_id) != ncch_data.end()) { + LOG_WARNING(Service_FS, "Registering program %016" PRIX64 + " with SelfNCCH will override existing mapping", + program_id); + } + + NCCHData& data = ncch_data[program_id]; + + std::shared_ptr romfs_file_; if (Loader::ResultStatus::Success == - app_loader.ReadRomFS(romfs_file, ncch_data.romfs_offset, ncch_data.romfs_size)) { + app_loader.ReadRomFS(romfs_file_, data.romfs_offset, data.romfs_size)) { - ncch_data.romfs_file = std::move(romfs_file); + data.romfs_file = std::move(romfs_file_); } std::shared_ptr update_romfs_file; if (Loader::ResultStatus::Success == - app_loader.ReadUpdateRomFS(update_romfs_file, ncch_data.update_romfs_offset, - ncch_data.update_romfs_size)) { + app_loader.ReadUpdateRomFS(update_romfs_file, data.update_romfs_offset, + data.update_romfs_size)) { - ncch_data.update_romfs_file = std::move(update_romfs_file); + data.update_romfs_file = std::move(update_romfs_file); } std::vector buffer; if (Loader::ResultStatus::Success == app_loader.ReadIcon(buffer)) - ncch_data.icon = std::make_shared>(std::move(buffer)); + data.icon = std::make_shared>(std::move(buffer)); buffer.clear(); if (Loader::ResultStatus::Success == app_loader.ReadLogo(buffer)) - ncch_data.logo = std::make_shared>(std::move(buffer)); + data.logo = std::make_shared>(std::move(buffer)); buffer.clear(); if (Loader::ResultStatus::Success == app_loader.ReadBanner(buffer)) - ncch_data.banner = std::make_shared>(std::move(buffer)); + data.banner = std::make_shared>(std::move(buffer)); } ResultVal> ArchiveFactory_SelfNCCH::Open(const Path& path) { - auto archive = std::make_unique(ncch_data); + auto archive = std::make_unique( + ncch_data[Kernel::g_current_process->codeset->program_id]); return MakeResult>(std::move(archive)); } diff --git a/src/core/file_sys/archive_selfncch.h b/src/core/file_sys/archive_selfncch.h index f1c659948..0d6d6766e 100644 --- a/src/core/file_sys/archive_selfncch.h +++ b/src/core/file_sys/archive_selfncch.h @@ -6,6 +6,7 @@ #include #include +#include #include #include "common/common_types.h" #include "core/file_sys/archive_backend.h" @@ -33,7 +34,10 @@ struct NCCHData { /// File system interface to the SelfNCCH archive class ArchiveFactory_SelfNCCH final : public ArchiveFactory { public: - explicit ArchiveFactory_SelfNCCH(Loader::AppLoader& app_loader); + ArchiveFactory_SelfNCCH() = default; + + /// Registers a loaded application so that we can open its SelfNCCH archive when requested. + void Register(Loader::AppLoader& app_loader); std::string GetName() const override { return "SelfNCCH"; @@ -43,7 +47,8 @@ public: ResultVal GetFormatInfo(const Path& path) const override; private: - NCCHData ncch_data; + /// Mapping of ProgramId -> NCCHData + std::unordered_map ncch_data; }; } // namespace FileSys diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 033fbc9aa..4ccb3cd32 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -20,6 +20,7 @@ #include "core/file_sys/archive_savedata.h" #include "core/file_sys/archive_sdmc.h" #include "core/file_sys/archive_sdmcwriteonly.h" +#include "core/file_sys/archive_selfncch.h" #include "core/file_sys/archive_systemsavedata.h" #include "core/file_sys/directory_backend.h" #include "core/file_sys/errors.h" @@ -48,7 +49,7 @@ struct hash { return std::hash()(static_cast(id_code)); } }; -} +} // namespace std static constexpr Kernel::Handle INVALID_HANDLE{}; @@ -564,6 +565,21 @@ void RegisterArchiveTypes() { auto systemsavedata_factory = std::make_unique(nand_directory); RegisterArchiveType(std::move(systemsavedata_factory), ArchiveIdCode::SystemSaveData); + + auto selfncch_factory = std::make_unique(); + RegisterArchiveType(std::move(selfncch_factory), ArchiveIdCode::SelfNCCH); +} + +void RegisterSelfNCCH(Loader::AppLoader& app_loader) { + auto itr = id_code_map.find(ArchiveIdCode::SelfNCCH); + if (itr == id_code_map.end()) { + LOG_ERROR(Service_FS, + "Could not register a new NCCH because the SelfNCCH archive hasn't been created"); + return; + } + + auto* factory = static_cast(itr->second.get()); + factory->Register(app_loader); } void UnregisterArchiveTypes() { diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index 3a3371c88..e3c8fc2ef 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -21,6 +21,10 @@ static constexpr char SYSTEM_ID[]{"00000000000000000000000000000000"}; /// The scrambled SD card CID, also known as ID1 static constexpr char SDCARD_ID[]{"00000000000000000000000000000000"}; +namespace Loader { +class AppLoader; +} + namespace Service { namespace FS { @@ -259,6 +263,9 @@ void ArchiveInit(); /// Shutdown archives void ArchiveShutdown(); +/// Registers a new NCCH file with the SelfNCCH archive factory +void RegisterSelfNCCH(Loader::AppLoader& app_loader); + /// Register all archive types void RegisterArchiveTypes(); diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index a03515e6e..5ad5c5287 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -278,8 +278,7 @@ ResultStatus AppLoader_THREEDSX::Load() { Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); - Service::FS::RegisterArchiveType(std::make_unique(*this), - Service::FS::ArchiveIdCode::SelfNCCH); + Service::FS::RegisterSelfNCCH(*this); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index c46d7cfc6..5107135f9 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -187,8 +187,7 @@ ResultStatus AppLoader_NCCH::Load() { if (ResultStatus::Success != result) return result; - Service::FS::RegisterArchiveType(std::make_unique(*this), - Service::FS::ArchiveIdCode::SelfNCCH); + Service::FS::RegisterSelfNCCH(*this); ParseRegionLockoutInfo(); -- cgit v1.2.3 From 41f6c9f87f3cd231954cd401be39653c4f78740a Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 24 Sep 2017 20:52:46 -0500 Subject: Memory/RasterizerCache: Ignore unmapped memory regions when caching physical regions. Not all physical regions need to be mapped into the address space of every process, for example, system modules do not have a VRAM mapping. This fixes a crash when loading applets and system modules. --- src/core/memory.cpp | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 68a6b1ac2..2f5cdcefe 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -316,8 +316,15 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { for (unsigned i = 0; i < num_pages; ++i, paddr += PAGE_SIZE) { boost::optional maybe_vaddr = PhysicalToVirtualAddress(paddr); - if (!maybe_vaddr) + // While the physical <-> virtual mapping is 1:1 for the regions supported by the cache, + // some games (like Pokemon Super Mystery Dungeon) will try to use textures that go beyond + // the end address of VRAM, causing the Virtual->Physical translation to fail when flushing + // parts of the texture. + if (!maybe_vaddr) { + LOG_ERROR(HW_Memory, + "Trying to flush a cached region to an invalid physical address %08X", paddr); continue; + } VAddr vaddr = *maybe_vaddr; u8& res_count = current_page_table->cached_res_count[vaddr >> PAGE_BITS]; @@ -329,6 +336,10 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { if (res_count == 0) { PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its address + // space, for example, a system module need not have a VRAM mapping. + break; case PageType::Memory: page_type = PageType::RasterizerCachedMemory; current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr; @@ -347,6 +358,10 @@ void RasterizerMarkRegionCached(PAddr start, u32 size, int count_delta) { if (res_count == 0) { PageType& page_type = current_page_table->attributes[vaddr >> PAGE_BITS]; switch (page_type) { + case PageType::Unmapped: + // It is not necessary for a process to have this region mapped into its address + // space, for example, a system module need not have a VRAM mapping. + break; case PageType::RasterizerCachedMemory: { u8* pointer = GetPointerFromVMA(vaddr & ~PAGE_MASK); if (pointer == nullptr) { -- cgit v1.2.3 From e27ae046960e20144892cf8252d8a672a48b0123 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 24 Sep 2017 19:09:13 -0500 Subject: HLE/APT: Always set up the APT parameter when starting a library applet. Only use the HLE interface if an HLE applet with the desired id was started. This commit reorganizes the APT code surrounding parameter creation and delivery to make it easier to support LLE applets in the future. As future work, the HLE applet interface can be reworked to utilize the same facilities as the LLE interface. --- src/core/hle/service/apt/apt.cpp | 73 +++++++++++++++++++++++--------------- src/core/hle/service/apt/apt_s.cpp | 4 +-- 2 files changed, 47 insertions(+), 30 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 8c0ba73f2..c36775473 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -165,7 +165,11 @@ void SendParameter(const MessageParameter& parameter) { next_parameter = parameter; // Signal the event to let the receiver know that a new parameter is ready to be read auto* const slot_data = GetAppletSlotData(static_cast(parameter.destination_id)); - ASSERT(slot_data); + if (slot_data == nullptr) { + LOG_DEBUG(Service_APT, "No applet was registered with the id %03X", + parameter.destination_id); + return; + } slot_data->parameter_event->Signal(); } @@ -486,9 +490,6 @@ void SendParameter(Service::Interface* self) { size_t size; VAddr buffer = rp.PopStaticBuffer(&size); - std::shared_ptr dest_applet = - HLE::Applets::Applet::Get(static_cast(dst_app_id)); - LOG_DEBUG(Service_APT, "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", @@ -503,12 +504,6 @@ void SendParameter(Service::Interface* self) { return; } - if (dest_applet == nullptr) { - LOG_ERROR(Service_APT, "Unknown applet id=0x%08X", dst_app_id); - rb.Push(-1); // TODO(Subv): Find the right error code - return; - } - MessageParameter param; param.destination_id = dst_app_id; param.sender_id = src_app_id; @@ -517,7 +512,14 @@ void SendParameter(Service::Interface* self) { param.buffer.resize(buffer_size); Memory::ReadBlock(buffer, param.buffer.data(), param.buffer.size()); - rb.Push(dest_applet->ReceiveParameter(param)); + SendParameter(param); + + // If the applet is running in HLE mode, use the HLE interface to communicate with it. + if (auto dest_applet = HLE::Applets::Applet::Get(static_cast(dst_app_id))) { + rb.Push(dest_applet->ReceiveParameter(param)); + } else { + rb.Push(RESULT_SUCCESS); + } } void ReceiveParameter(Service::Interface* self) { @@ -746,7 +748,12 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 1, 0); // 0x180040 AppletId applet_id = static_cast(rp.Pop()); + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + // TODO(Subv): Launch the requested applet application. + auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); @@ -754,14 +761,18 @@ void PrepareToStartLibraryApplet(Service::Interface* self) { } else { rb.Push(HLE::Applets::Applet::Create(applet_id)); } - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); } void PreloadLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x16, 1, 0); // 0x160040 AppletId applet_id = static_cast(rp.Pop()); + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + + // TODO(Subv): Launch the requested applet application. + auto applet = HLE::Applets::Applet::Get(applet_id); if (applet) { LOG_WARNING(Service_APT, "applet has already been started id=%08X", applet_id); @@ -769,34 +780,40 @@ void PreloadLibraryApplet(Service::Interface* self) { } else { rb.Push(HLE::Applets::Applet::Create(applet_id)); } - LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); } void StartLibraryApplet(Service::Interface* self) { IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x1E, 2, 4); // 0x1E0084 AppletId applet_id = static_cast(rp.Pop()); - std::shared_ptr 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); - IPC::RequestBuilder rb = rp.MakeBuilder(1, 0, false); - rb.Push(-1); // TODO(Subv): Find the right error code - return; - } size_t buffer_size = rp.Pop(); Kernel::Handle handle = rp.PopHandle(); VAddr buffer_addr = rp.PopStaticBuffer(); - AppletStartupParameter parameter; - parameter.object = Kernel::g_handle_table.GetGeneric(handle); - parameter.buffer.resize(buffer_size); - Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size()); + LOG_DEBUG(Service_APT, "called applet_id=%08X", applet_id); IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - rb.Push(applet->Start(parameter)); + + // Send the Wakeup signal to the applet + MessageParameter param; + param.destination_id = static_cast(applet_id); + param.sender_id = static_cast(AppletId::Application); + param.object = Kernel::g_handle_table.GetGeneric(handle); + param.signal = static_cast(SignalType::Wakeup); + param.buffer.resize(buffer_size); + Memory::ReadBlock(buffer_addr, param.buffer.data(), param.buffer.size()); + SendParameter(param); + + // In case the applet is being HLEd, attempt to communicate with it. + if (auto applet = HLE::Applets::Applet::Get(applet_id)) { + AppletStartupParameter parameter; + parameter.object = Kernel::g_handle_table.GetGeneric(handle); + parameter.buffer.resize(buffer_size); + Memory::ReadBlock(buffer_addr, parameter.buffer.data(), parameter.buffer.size()); + rb.Push(applet->Start(parameter)); + } else { + rb.Push(RESULT_SUCCESS); + } } void CancelLibraryApplet(Service::Interface* self) { diff --git a/src/core/hle/service/apt/apt_s.cpp b/src/core/hle/service/apt/apt_s.cpp index ec5668d05..cf74c2a36 100644 --- a/src/core/hle/service/apt/apt_s.cpp +++ b/src/core/hle/service/apt/apt_s.cpp @@ -20,7 +20,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00090040, nullptr, "IsRegistered"}, {0x000A0040, nullptr, "GetAttribute"}, {0x000B0040, InquireNotification, "InquireNotification"}, - {0x000C0104, nullptr, "SendParameter"}, + {0x000C0104, SendParameter, "SendParameter"}, {0x000D0080, ReceiveParameter, "ReceiveParameter"}, {0x000E0080, GlanceParameter, "GlanceParameter"}, {0x000F0100, nullptr, "CancelParameter"}, @@ -38,7 +38,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x001B00C4, nullptr, "StartApplication"}, {0x001C0000, nullptr, "WakeupApplication"}, {0x001D0000, nullptr, "CancelApplication"}, - {0x001E0084, nullptr, "StartLibraryApplet"}, + {0x001E0084, StartLibraryApplet, "StartLibraryApplet"}, {0x001F0084, nullptr, "StartSystemApplet"}, {0x00200044, nullptr, "StartNewestHomeMenu"}, {0x00210000, nullptr, "OrderToCloseApplication"}, -- cgit v1.2.3 From 35da7f57efd5153be37a05ffcbb57412da74265a Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 26 Sep 2017 17:27:44 -0500 Subject: Memory: Allow IsValidVirtualAddress to be called with a specific process parameter. There is still an overload of IsValidVirtualAddress that only takes the VAddr and will default to the current process. --- src/core/memory.cpp | 25 ++++++++++++++++++------- src/core/memory.h | 7 +++++++ 2 files changed, 25 insertions(+), 7 deletions(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index a6b5f6c99..c42f4326b 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -110,8 +110,8 @@ static u8* GetPointerFromVMA(VAddr vaddr) { /** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ -static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { - for (const auto& region : current_page_table->special_regions) { +static MMIORegionPointer GetMMIOHandler(const PageTable& page_table, VAddr vaddr) { + for (const auto& region : page_table.special_regions) { if (vaddr >= region.base && vaddr < (region.base + region.size)) { return region.handler; } @@ -120,6 +120,11 @@ static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { return nullptr; // Should never happen } +static MMIORegionPointer GetMMIOHandler(VAddr vaddr) { + const PageTable& page_table = Kernel::g_current_process->vm_manager.page_table; + return GetMMIOHandler(page_table, vaddr); +} + template T ReadMMIO(MMIORegionPointer mmio_handler, VAddr addr); @@ -204,18 +209,20 @@ void Write(const VAddr vaddr, const T data) { } } -bool IsValidVirtualAddress(const VAddr vaddr) { - const u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS]; +bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) { + auto& page_table = process.vm_manager.page_table; + + const u8* page_pointer = page_table.pointers[vaddr >> PAGE_BITS]; if (page_pointer) return true; - if (current_page_table->attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) + if (page_table.attributes[vaddr >> PAGE_BITS] == PageType::RasterizerCachedMemory) return true; - if (current_page_table->attributes[vaddr >> PAGE_BITS] != PageType::Special) + if (page_table.attributes[vaddr >> PAGE_BITS] != PageType::Special) return false; - MMIORegionPointer mmio_region = GetMMIOHandler(vaddr); + MMIORegionPointer mmio_region = GetMMIOHandler(page_table, vaddr); if (mmio_region) { return mmio_region->IsValidAddress(vaddr); } @@ -223,6 +230,10 @@ bool IsValidVirtualAddress(const VAddr vaddr) { return false; } +bool IsValidVirtualAddress(const VAddr vaddr) { + return IsValidVirtualAddress(*Kernel::g_current_process, vaddr); +} + bool IsValidPhysicalAddress(const PAddr paddr) { return GetPhysicalPointer(paddr) != nullptr; } diff --git a/src/core/memory.h b/src/core/memory.h index 1865bfea0..347c08c78 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -12,6 +12,10 @@ #include "common/common_types.h" #include "core/mmio.h" +namespace Kernel { +class Process; +} + namespace Memory { /** @@ -185,7 +189,10 @@ enum : VAddr { void SetCurrentPageTable(PageTable* page_table); PageTable* GetCurrentPageTable(); +/// Determines if the given VAddr is valid for the specified process. +bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr); bool IsValidVirtualAddress(const VAddr addr); + bool IsValidPhysicalAddress(const PAddr addr); u8 Read8(VAddr addr); -- cgit v1.2.3 From 3165466b665185ecbc3e33b02b0b90e25e7248ba Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 26 Sep 2017 17:40:49 -0500 Subject: Kernel/Thread: Allow specifying which process a thread belongs to when creating it. Don't automatically assume that Thread::Create will only be called when the parent process is currently scheduled. This assumption will be broken when applets or system modules are loaded. --- src/core/hle/kernel/process.cpp | 2 +- src/core/hle/kernel/thread.cpp | 17 +++++++++-------- src/core/hle/kernel/thread.h | 15 +++++++++------ src/core/hle/svc.cpp | 5 +++-- 4 files changed, 22 insertions(+), 17 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 522ad2333..cf3163e0f 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -147,7 +147,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { } vm_manager.LogLayout(Log::Level::Debug); - Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); + Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority, this); } VAddr Process::GetLinearHeapAreaAddress() const { diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 61378211f..1033f8552 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -361,7 +361,8 @@ static void ResetThreadContext(ARM_Interface::ThreadContext& context, u32 stack_ } ResultVal> Thread::Create(std::string name, VAddr entry_point, u32 priority, - u32 arg, s32 processor_id, VAddr stack_top) { + u32 arg, s32 processor_id, VAddr stack_top, + SharedPtr owner_process) { // Check if priority is in ranged. Lowest priority -> highest priority id. if (priority > THREADPRIO_LOWEST) { LOG_ERROR(Kernel_SVC, "Invalid thread priority: %d", priority); @@ -375,7 +376,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, // TODO(yuriks): Other checks, returning 0xD9001BEA - if (!Memory::IsValidVirtualAddress(entry_point)) { + if (!Memory::IsValidVirtualAddress(*owner_process, entry_point)) { LOG_ERROR(Kernel_SVC, "(name=%s): invalid entry %08x", name.c_str(), entry_point); // TODO: Verify error return ResultCode(ErrorDescription::InvalidAddress, ErrorModule::Kernel, @@ -399,10 +400,10 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->wait_address = 0; thread->name = std::move(name); thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); - thread->owner_process = g_current_process; + thread->owner_process = owner_process; // Find the next available TLS index, and mark it as used - auto& tls_slots = Kernel::g_current_process->tls_slots; + auto& tls_slots = owner_process->tls_slots; bool needs_allocation = true; u32 available_page; // Which allocated page has free space u32 available_slot; // Which slot within the page is free @@ -426,13 +427,13 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, // Allocate some memory from the end of the linear heap for this region. linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); memory_region->used += Memory::PAGE_SIZE; - Kernel::g_current_process->linear_heap_used += Memory::PAGE_SIZE; + owner_process->linear_heap_used += Memory::PAGE_SIZE; tls_slots.emplace_back(0); // The page is completely available at the start available_page = tls_slots.size() - 1; available_slot = 0; // Use the first slot in the new page - auto& vm_manager = Kernel::g_current_process->vm_manager; + auto& vm_manager = owner_process->vm_manager; vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); // Map the page to the current process' address space. @@ -486,10 +487,10 @@ void Thread::BoostPriority(s32 priority) { current_priority = priority; } -SharedPtr SetupMainThread(u32 entry_point, s32 priority) { +SharedPtr SetupMainThread(u32 entry_point, s32 priority, SharedPtr owner_process) { // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, - Memory::HEAP_VADDR_END); + Memory::HEAP_VADDR_END, owner_process); SharedPtr thread = std::move(thread_res).Unwrap(); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 6a3566f15..ddc0d15c5 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -56,10 +56,12 @@ public: * @param arg User data to pass to the thread * @param processor_id The ID(s) of the processors on which the thread is desired to be run * @param stack_top The address of the thread's stack top + * @param owner_process The parent process for the thread * @return A shared pointer to the newly created thread */ static ResultVal> Create(std::string name, VAddr entry_point, u32 priority, - u32 arg, s32 processor_id, VAddr stack_top); + u32 arg, s32 processor_id, VAddr stack_top, + SharedPtr owner_process); std::string GetName() const override { return name; @@ -116,9 +118,9 @@ public: void ResumeFromWait(); /** - * Schedules an event to wake up the specified thread after the specified delay - * @param nanoseconds The time this thread will be allowed to sleep for - */ + * Schedules an event to wake up the specified thread after the specified delay + * @param nanoseconds The time this thread will be allowed to sleep for + */ void WakeAfterDelay(s64 nanoseconds); /** @@ -214,9 +216,10 @@ private: * Sets up the primary application thread * @param entry_point The address at which the thread should start execution * @param priority The priority to give the main thread + * @param owner_process The parent process for the main thread * @return A shared pointer to the main thread */ -SharedPtr SetupMainThread(u32 entry_point, s32 priority); +SharedPtr SetupMainThread(u32 entry_point, s32 priority, SharedPtr owner_process); /** * Returns whether there are any threads that are ready to run. @@ -276,4 +279,4 @@ void ThreadingShutdown(); */ const std::vector>& GetThreadList(); -} // namespace +} // namespace Kernel diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index dfc36748c..05c6897bf 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -656,8 +656,9 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, u32 priority, u32 ent "Newly created thread must run in the SysCore (Core1), unimplemented."); } - CASCADE_RESULT(SharedPtr thread, Kernel::Thread::Create(name, entry_point, priority, - arg, processor_id, stack_top)); + CASCADE_RESULT(SharedPtr thread, + Kernel::Thread::Create(name, entry_point, priority, arg, processor_id, stack_top, + Kernel::g_current_process)); thread->context.fpscr = FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO; // 0x03C00000 -- cgit v1.2.3 From 7f48aa8d2580da6b3b83a389e31804e493aba69f Mon Sep 17 00:00:00 2001 From: Subv Date: Tue, 26 Sep 2017 18:17:47 -0500 Subject: Loaders: Don't automatically set the current process every time we load an application. The loaders will now just create a Kernel::Process, construct it and return it to the caller, which is responsible for setting it as the current process and configuring the global page table. --- src/core/core.cpp | 6 ++++-- src/core/loader/3dsx.cpp | 15 +++++++-------- src/core/loader/3dsx.h | 2 +- src/core/loader/elf.cpp | 15 +++++++-------- src/core/loader/elf.h | 2 +- src/core/loader/loader.h | 13 ++++++++----- src/core/loader/ncch.cpp | 19 +++++++++---------- src/core/loader/ncch.h | 5 +++-- 8 files changed, 40 insertions(+), 37 deletions(-) (limited to 'src/core') diff --git a/src/core/core.cpp b/src/core/core.cpp index 59b8768e7..0c7a72987 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -13,6 +13,7 @@ #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" #include "core/hw/hw.h" @@ -100,7 +101,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file return init_result; } - const Loader::ResultStatus load_result{app_loader->Load()}; + const Loader::ResultStatus load_result{app_loader->Load(Kernel::g_current_process)}; if (Loader::ResultStatus::Success != load_result) { LOG_CRITICAL(Core, "Failed to load ROM (Error %i)!", load_result); System::Shutdown(); @@ -114,6 +115,7 @@ System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& file return ResultStatus::ErrorLoader; } } + Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); status = ResultStatus::Success; return status; } @@ -196,4 +198,4 @@ void System::Shutdown() { LOG_DEBUG(Core, "Shutdown OK"); } -} // namespace +} // namespace Core diff --git a/src/core/loader/3dsx.cpp b/src/core/loader/3dsx.cpp index 5ad5c5287..918038f1e 100644 --- a/src/core/loader/3dsx.cpp +++ b/src/core/loader/3dsx.cpp @@ -91,8 +91,8 @@ static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets) return loadinfo->seg_addrs[2] + addr - offsets[1]; } -using Kernel::SharedPtr; using Kernel::CodeSet; +using Kernel::SharedPtr; static THREEDSX_Error Load3DSXFile(FileUtil::IOFile& file, u32 base_addr, SharedPtr* out_codeset) { @@ -255,7 +255,7 @@ FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -ResultStatus AppLoader_THREEDSX::Load() { +ResultStatus AppLoader_THREEDSX::Load(Kernel::SharedPtr& process) { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; @@ -267,16 +267,15 @@ ResultStatus AppLoader_THREEDSX::Load() { return ResultStatus::Error; codeset->name = filename; - Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); - Kernel::g_current_process->svc_access_mask.set(); - Kernel::g_current_process->address_mappings = default_address_mappings; - Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + process = Kernel::Process::Create(std::move(codeset)); + process->svc_access_mask.set(); + process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process - Kernel::g_current_process->resource_limit = + process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); + process->Run(48, Kernel::DEFAULT_STACK_SIZE); Service::FS::RegisterSelfNCCH(*this); diff --git a/src/core/loader/3dsx.h b/src/core/loader/3dsx.h index 3f376778a..1e59bbb9d 100644 --- a/src/core/loader/3dsx.h +++ b/src/core/loader/3dsx.h @@ -31,7 +31,7 @@ public: return IdentifyType(file); } - ResultStatus Load() override; + ResultStatus Load(Kernel::SharedPtr& process) override; ResultStatus ReadIcon(std::vector& buffer) override; diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 2de1f4e81..e36e42120 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -13,8 +13,8 @@ #include "core/loader/elf.h" #include "core/memory.h" -using Kernel::SharedPtr; using Kernel::CodeSet; +using Kernel::SharedPtr; //////////////////////////////////////////////////////////////////////////////////////////////////// // ELF Header Constants @@ -375,7 +375,7 @@ FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file) { return FileType::Error; } -ResultStatus AppLoader_ELF::Load() { +ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr& process) { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; @@ -394,16 +394,15 @@ ResultStatus AppLoader_ELF::Load() { SharedPtr codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); codeset->name = filename; - Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); - Kernel::g_current_process->svc_access_mask.set(); - Kernel::g_current_process->address_mappings = default_address_mappings; - Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + process = Kernel::Process::Create(std::move(codeset)); + process->svc_access_mask.set(); + process->address_mappings = default_address_mappings; // Attach the default resource limit (APPLICATION) to the process - Kernel::g_current_process->resource_limit = + process->resource_limit = Kernel::ResourceLimit::GetForCategory(Kernel::ResourceLimitCategory::APPLICATION); - Kernel::g_current_process->Run(48, Kernel::DEFAULT_STACK_SIZE); + process->Run(48, Kernel::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 862aa90d8..113da5917 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -30,7 +30,7 @@ public: return IdentifyType(file); } - ResultStatus Load() override; + ResultStatus Load(Kernel::SharedPtr& process) override; private: std::string filename; diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 3160fd2fd..82b2be6a3 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -13,10 +13,12 @@ #include #include "common/common_types.h" #include "common/file_util.h" +#include "core/hle/kernel/kernel.h" namespace Kernel { struct AddressMapping; -} +class Process; +} // namespace Kernel //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -92,10 +94,11 @@ public: virtual FileType GetFileType() = 0; /** - * Load the application - * @return ResultStatus result of function + * Load the application and return the created Process instance + * @param process The newly created process. + * @return The status result of the operation. */ - virtual ResultStatus Load() = 0; + virtual ResultStatus Load(Kernel::SharedPtr& process) = 0; /** * Loads the system mode that this application needs. @@ -206,4 +209,4 @@ extern const std::initializer_list default_address_mappi */ std::unique_ptr GetLoader(const std::string& filename); -} // namespace +} // namespace Loader diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 5107135f9..66bc5823d 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -67,9 +67,9 @@ std::pair, ResultStatus> AppLoader_NCCH::LoadKernelSystemMo ResultStatus::Success); } -ResultStatus AppLoader_NCCH::LoadExec() { - using Kernel::SharedPtr; +ResultStatus AppLoader_NCCH::LoadExec(Kernel::SharedPtr& process) { using Kernel::CodeSet; + using Kernel::SharedPtr; if (!is_loaded) return ResultStatus::ErrorNotLoaded; @@ -107,16 +107,15 @@ ResultStatus AppLoader_NCCH::LoadExec() { codeset->entrypoint = codeset->code.addr; codeset->memory = std::make_shared>(std::move(code)); - Kernel::g_current_process = Kernel::Process::Create(std::move(codeset)); - Memory::SetCurrentPageTable(&Kernel::g_current_process->vm_manager.page_table); + process = Kernel::Process::Create(std::move(codeset)); // Attach a resource limit to the process based on the resource limit category - Kernel::g_current_process->resource_limit = + process->resource_limit = Kernel::ResourceLimit::GetForCategory(static_cast( overlay_ncch->exheader_header.arm11_system_local_caps.resource_limit_category)); // Set the default CPU core for this process - Kernel::g_current_process->ideal_processor = + process->ideal_processor = overlay_ncch->exheader_header.arm11_system_local_caps.ideal_processor; // Copy data while converting endianness @@ -124,11 +123,11 @@ ResultStatus AppLoader_NCCH::LoadExec() { kernel_caps; std::copy_n(overlay_ncch->exheader_header.arm11_kernel_caps.descriptors, kernel_caps.size(), begin(kernel_caps)); - Kernel::g_current_process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); + process->ParseKernelCaps(kernel_caps.data(), kernel_caps.size()); s32 priority = overlay_ncch->exheader_header.arm11_system_local_caps.priority; u32 stack_size = overlay_ncch->exheader_header.codeset_info.stack_size; - Kernel::g_current_process->Run(priority, stack_size); + process->Run(priority, stack_size); return ResultStatus::Success; } return ResultStatus::Error; @@ -151,7 +150,7 @@ void AppLoader_NCCH::ParseRegionLockoutInfo() { } } -ResultStatus AppLoader_NCCH::Load() { +ResultStatus AppLoader_NCCH::Load(Kernel::SharedPtr& process) { u64_le ncch_program_id; if (is_loaded) @@ -183,7 +182,7 @@ ResultStatus AppLoader_NCCH::Load() { is_loaded = true; // Set state to loaded - result = LoadExec(); // Load the executable into memory for booting + result = LoadExec(process); // Load the executable into memory for booting if (ResultStatus::Success != result) return result; diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 9b56465cb..09230ae33 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -33,7 +33,7 @@ public: return IdentifyType(file); } - ResultStatus Load() override; + ResultStatus Load(Kernel::SharedPtr& process) override; /** * Loads the Exheader and returns the system mode for this application. @@ -62,9 +62,10 @@ public: private: /** * Loads .code section into memory for booting + * @param process The newly created process * @return ResultStatus result of function */ - ResultStatus LoadExec(); + ResultStatus LoadExec(Kernel::SharedPtr& process); /// Reads the region lockout info in the SMDH and send it to CFG service void ParseRegionLockoutInfo(); -- cgit v1.2.3 From 8432749db7afecc9beea20f993cc036418caaa15 Mon Sep 17 00:00:00 2001 From: Subv Date: Thu, 28 Sep 2017 11:53:32 -0500 Subject: Kernel/Threads: When putting a thread to wait, specify a function to execute when it is awoken. This change makes for a clearer (less confusing) path of execution in the scheduler, now the code to execute when a thread awakes is closer to the code that puts the thread to sleep (WaitSynch1, WaitSynchN). It also allows us to implement the special wake up behavior of ReplyAndReceive without hacking up WaitObject::WakeupAllWaitingThreads. If savestates are desired in the future, we can change this implementation to one similar to the CoreTiming event system, where we first register the callback functions at startup and assign their identifiers to the Thread callback variable instead of directly assigning a lambda to the wake up callback variable. --- src/core/hle/kernel/thread.cpp | 13 +++++-- src/core/hle/kernel/thread.h | 15 ++++++-- src/core/hle/kernel/wait_object.cpp | 11 +++--- src/core/hle/svc.cpp | 69 ++++++++++++++++++++++++++++++++++--- 4 files changed, 91 insertions(+), 17 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 61378211f..690cb20b3 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -247,12 +247,15 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) { if (thread->status == THREADSTATUS_WAIT_SYNCH_ANY || thread->status == THREADSTATUS_WAIT_SYNCH_ALL || thread->status == THREADSTATUS_WAIT_ARB) { - thread->wait_set_output = false; + + // Invoke the wakeup callback before clearing the wait objects + if (thread->wakeup_callback) + thread->wakeup_callback(ThreadWakeupReason::Timeout, thread, nullptr); + // Remove the thread from each of its waiting objects' waitlists for (auto& object : thread->wait_objects) object->RemoveWaitingThread(thread.get()); thread->wait_objects.clear(); - thread->SetWaitSynchronizationResult(RESULT_TIMEOUT); } thread->ResumeFromWait(); @@ -278,6 +281,9 @@ void Thread::ResumeFromWait() { break; case THREADSTATUS_READY: + // The thread's wakeup callback must have already been cleared when the thread was first + // awoken. + ASSERT(wakeup_callback == nullptr); // If the thread is waiting on multiple wait objects, it might be awoken more than once // before actually resuming. We can ignore subsequent wakeups if the thread status has // already been set to THREADSTATUS_READY. @@ -293,6 +299,8 @@ void Thread::ResumeFromWait() { return; } + wakeup_callback = nullptr; + ready_queue.push_back(current_priority, this); status = THREADSTATUS_READY; Core::System::GetInstance().PrepareReschedule(); @@ -394,7 +402,6 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, thread->nominal_priority = thread->current_priority = priority; thread->last_running_ticks = CoreTiming::GetTicks(); thread->processor_id = processor_id; - thread->wait_set_output = false; thread->wait_objects.clear(); thread->wait_address = 0; thread->name = std::move(name); diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index 6a3566f15..328f1a86a 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -41,6 +41,11 @@ enum ThreadStatus { THREADSTATUS_DEAD ///< Run to completion, or forcefully terminated }; +enum class ThreadWakeupReason { + Signal, // The thread was woken up by WakeupAllWaitingThreads due to an object signal. + Timeout // The thread was woken up due to a wait timeout. +}; + namespace Kernel { class Mutex; @@ -197,14 +202,18 @@ public: VAddr wait_address; ///< If waiting on an AddressArbiter, this is the arbitration address - /// True if the WaitSynchronizationN output parameter should be set on thread wakeup. - bool wait_set_output; - std::string name; /// Handle used as userdata to reference this object when inserting into the CoreTiming queue. Handle callback_handle; + using WakeupCallback = void(ThreadWakeupReason reason, SharedPtr thread, + SharedPtr object); + // Callback that will be invoked when the thread is resumed from a waiting state. If the thread + // was waiting via WaitSynchronizationN then the object will be the last object that became + // available. In case of a timeout, the object will be nullptr. + std::function wakeup_callback; + private: Thread(); ~Thread() override; diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index f245eda6c..1ced26905 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -71,23 +71,20 @@ void WaitObject::WakeupAllWaitingThreads() { while (auto thread = GetHighestPriorityReadyThread()) { if (!thread->IsSleepingOnWaitAll()) { Acquire(thread.get()); - // Set the output index of the WaitSynchronizationN call to the index of this object. - if (thread->wait_set_output) { - thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(this)); - thread->wait_set_output = false; - } } else { for (auto& object : thread->wait_objects) { object->Acquire(thread.get()); } - // Note: This case doesn't update the output index of WaitSynchronizationN. } + // Invoke the wakeup callback before clearing the wait objects + if (thread->wakeup_callback) + thread->wakeup_callback(ThreadWakeupReason::Signal, thread, this); + for (auto& object : thread->wait_objects) object->RemoveWaitingThread(thread.get()); thread->wait_objects.clear(); - thread->SetWaitSynchronizationResult(RESULT_SUCCESS); thread->ResumeFromWait(); } } diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index dfc36748c..41e62cf62 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -271,6 +271,24 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr thread, + Kernel::SharedPtr object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + + // WaitSynchronization1 doesn't have an output index like WaitSynchronizationN, so we + // don't have to do anything else here. + }; + Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread @@ -344,6 +362,23 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr thread, + Kernel::SharedPtr object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ALL); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + // The wait_all case does not update the output index. + }; + Core::System::GetInstance().PrepareReschedule(); // This value gets set to -1 by default in this case, it is not modified after this. @@ -389,12 +424,28 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // Create an event to wake the thread up after the specified nanosecond delay has passed thread->WakeAfterDelay(nano_seconds); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr thread, + Kernel::SharedPtr object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); + + if (reason == ThreadWakeupReason::Timeout) { + thread->SetWaitSynchronizationResult(Kernel::RESULT_TIMEOUT); + return; + } + + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); + }; + Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects. // Otherwise we retain the default value of timeout, and -1 in the out parameter - thread->wait_set_output = true; *out = -1; return Kernel::RESULT_TIMEOUT; } @@ -483,8 +534,6 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl // No objects were ready to be acquired, prepare to suspend the thread. - // TODO(Subv): Perform IPC translation upon wakeup. - // Put the thread to sleep thread->status = THREADSTATUS_WAIT_SYNCH_ANY; @@ -496,12 +545,24 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl thread->wait_objects = std::move(objects); + thread->wakeup_callback = [](ThreadWakeupReason reason, + Kernel::SharedPtr thread, + Kernel::SharedPtr object) { + + ASSERT(thread->status == THREADSTATUS_WAIT_SYNCH_ANY); + ASSERT(reason == ThreadWakeupReason::Signal); + + thread->SetWaitSynchronizationResult(RESULT_SUCCESS); + thread->SetWaitSynchronizationOutput(thread->GetWaitObjectIndex(object.get())); + + // TODO(Subv): Perform IPC translation upon wakeup. + }; + Core::System::GetInstance().PrepareReschedule(); // Note: The output of this SVC will be set to RESULT_SUCCESS if the thread resumes due to a // signal in one of its wait objects, or to 0xC8A01836 if there was a translation error. // By default the index is set to -1. - thread->wait_set_output = true; *index = -1; return RESULT_SUCCESS; } -- cgit v1.2.3 From a13ab958cbba75bc9abd1ca50f3030a10a75784e Mon Sep 17 00:00:00 2001 From: Huw Pascoe Date: Wed, 27 Sep 2017 00:26:09 +0100 Subject: Fixed type conversion ambiguity --- src/core/gdbstub/gdbstub.cpp | 4 ++-- src/core/hle/ipc.h | 8 +++---- src/core/hle/ipc_helpers.h | 12 +++++------ src/core/hle/kernel/hle_ipc.cpp | 2 +- src/core/hle/kernel/mutex.cpp | 2 +- src/core/hle/kernel/resource_limit.cpp | 2 +- src/core/hle/kernel/resource_limit.h | 2 +- src/core/hle/kernel/shared_memory.cpp | 3 ++- src/core/hle/kernel/shared_memory.h | 2 +- src/core/hle/kernel/thread.cpp | 18 ++++++++-------- src/core/hle/kernel/thread.h | 14 ++++++------- src/core/hle/kernel/wait_object.cpp | 2 +- src/core/hle/service/apt/apt.cpp | 4 ++-- src/core/hle/service/cam/cam.cpp | 2 +- src/core/hle/service/cfg/cfg.cpp | 2 +- src/core/hle/service/fs/archive.cpp | 2 +- src/core/hle/service/hid/hid.cpp | 2 +- src/core/hle/service/ldr_ro/cro_helper.h | 6 ++++-- src/core/hle/service/nwm/nwm_uds.cpp | 10 ++++----- src/core/hle/service/nwm/uds_beacon.cpp | 4 ++-- src/core/hle/service/nwm/uds_data.cpp | 8 +++---- src/core/hle/svc.cpp | 8 +++---- src/core/memory.cpp | 36 +++++++++++++++++++------------- 23 files changed, 83 insertions(+), 72 deletions(-) (limited to 'src/core') diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 123fe7cd4..be2b2e25f 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -946,7 +946,7 @@ static void Init(u16 port) { WSAStartup(MAKEWORD(2, 2), &InitData); #endif - int tmpsock = socket(PF_INET, SOCK_STREAM, 0); + int tmpsock = static_cast(socket(PF_INET, SOCK_STREAM, 0)); if (tmpsock == -1) { LOG_ERROR(Debug_GDBStub, "Failed to create gdb socket"); } @@ -973,7 +973,7 @@ static void Init(u16 port) { sockaddr_in saddr_client; sockaddr* client_addr = reinterpret_cast(&saddr_client); socklen_t client_addrlen = sizeof(saddr_client); - gdbserver_socket = accept(tmpsock, client_addr, &client_addrlen); + gdbserver_socket = static_cast(accept(tmpsock, client_addr, &client_addrlen)); if (gdbserver_socket < 0) { // In the case that we couldn't start the server for whatever reason, just start CPU // execution like normal. diff --git a/src/core/hle/ipc.h b/src/core/hle/ipc.h index f7f96125a..87ed85df6 100644 --- a/src/core/hle/ipc.h +++ b/src/core/hle/ipc.h @@ -122,11 +122,11 @@ union StaticBufferDescInfo { BitField<14, 18, u32> size; }; -inline u32 StaticBufferDesc(u32 size, u8 buffer_id) { +inline u32 StaticBufferDesc(size_t size, u8 buffer_id) { StaticBufferDescInfo info{}; info.descriptor_type.Assign(StaticBuffer); info.buffer_id.Assign(buffer_id); - info.size.Assign(size); + info.size.Assign(static_cast(size)); return info.raw; } @@ -160,11 +160,11 @@ union MappedBufferDescInfo { BitField<4, 28, u32> size; }; -inline u32 MappedBufferDesc(u32 size, MappedBufferPermissions perms) { +inline u32 MappedBufferDesc(size_t size, MappedBufferPermissions perms) { MappedBufferDescInfo info{}; info.flags.Assign(MappedBuffer); info.perms.Assign(perms); - info.size.Assign(size); + info.size.Assign(static_cast(size)); return info.raw; } diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h index f0d89cffe..7cb95cbac 100644 --- a/src/core/hle/ipc_helpers.h +++ b/src/core/hle/ipc_helpers.h @@ -117,9 +117,9 @@ public: void PushCurrentPIDHandle(); - void PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id); + void PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id); - void PushMappedBuffer(VAddr buffer_vaddr, u32 size, MappedBufferPermissions perms); + void PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms); }; /// Push /// @@ -190,12 +190,12 @@ inline void RequestBuilder::PushCurrentPIDHandle() { Push(u32(0)); } -inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, u32 size, u8 buffer_id) { +inline void RequestBuilder::PushStaticBuffer(VAddr buffer_vaddr, size_t size, u8 buffer_id) { Push(StaticBufferDesc(size, buffer_id)); Push(buffer_vaddr); } -inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, u32 size, +inline void RequestBuilder::PushMappedBuffer(VAddr buffer_vaddr, size_t size, MappedBufferPermissions perms) { Push(MappedBufferDesc(size, perms)); Push(buffer_vaddr); @@ -227,8 +227,8 @@ public: bool validateHeader = true) { if (validateHeader) ValidateHeader(); - Header builderHeader{ - MakeHeader(header.command_id, normal_params_size, translate_params_size)}; + Header builderHeader{MakeHeader(static_cast(header.command_id), normal_params_size, + translate_params_size)}; if (context != nullptr) return {*context, builderHeader}; else diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp index 5ebe2eca4..6020e9764 100644 --- a/src/core/hle/kernel/hle_ipc.cpp +++ b/src/core/hle/kernel/hle_ipc.cpp @@ -37,7 +37,7 @@ SharedPtr HLERequestContext::GetIncomingHandle(u32 id_from_cmdbuf) const u32 HLERequestContext::AddOutgoingHandle(SharedPtr object) { request_handles.push_back(std::move(object)); - return request_handles.size() - 1; + return static_cast(request_handles.size() - 1); } void HLERequestContext::ClearIncomingObjects() { diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp index cef961289..2cbca5e5b 100644 --- a/src/core/hle/kernel/mutex.cpp +++ b/src/core/hle/kernel/mutex.cpp @@ -90,7 +90,7 @@ void Mutex::UpdatePriority() { if (!holding_thread) return; - s32 best_priority = THREADPRIO_LOWEST; + u32 best_priority = THREADPRIO_LOWEST; for (auto& waiter : GetWaitingThreads()) { if (waiter->current_priority < best_priority) best_priority = waiter->current_priority; diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp index a8f10a3ee..517dc47a8 100644 --- a/src/core/hle/kernel/resource_limit.cpp +++ b/src/core/hle/kernel/resource_limit.cpp @@ -61,7 +61,7 @@ s32 ResourceLimit::GetCurrentResourceValue(u32 resource) const { } } -s32 ResourceLimit::GetMaxResourceValue(u32 resource) const { +u32 ResourceLimit::GetMaxResourceValue(u32 resource) const { switch (resource) { case PRIORITY: return max_priority; diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h index 6cdfbcf8d..42874eb8d 100644 --- a/src/core/hle/kernel/resource_limit.h +++ b/src/core/hle/kernel/resource_limit.h @@ -67,7 +67,7 @@ public: * @param resource Requested resource type * @returns The max value of the resource type */ - s32 GetMaxResourceValue(u32 resource) const; + u32 GetMaxResourceValue(u32 resource) const; /// Name of resource limit object. std::string name; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index a7b66142f..02d5a7a36 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -42,7 +42,8 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u memory_region->used += size; shared_memory->linear_heap_phys_address = - Memory::FCRAM_PADDR + memory_region->base + shared_memory->backing_block_offset; + Memory::FCRAM_PADDR + memory_region->base + + static_cast(shared_memory->backing_block_offset); // Increase the amount of used linear heap memory for the owner process. if (shared_memory->owner_process != nullptr) { diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h index 94b335ed1..93a6f2182 100644 --- a/src/core/hle/kernel/shared_memory.h +++ b/src/core/hle/kernel/shared_memory.h @@ -114,7 +114,7 @@ public: /// Backing memory for this shared memory block. std::shared_ptr> backing_block; /// Offset into the backing block for this shared memory. - u32 backing_block_offset; + size_t backing_block_offset; /// Size of the memory block. Page-aligned. u32 size; /// Permission restrictions applied to the process which created the block. diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 1033f8552..11f7d2127 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -111,7 +111,7 @@ void Thread::Stop() { Thread* ArbitrateHighestPriorityThread(u32 address) { Thread* highest_priority_thread = nullptr; - s32 priority = THREADPRIO_LOWEST; + u32 priority = THREADPRIO_LOWEST; // Iterate through threads, find highest priority thread that is waiting to be arbitrated... for (auto& thread : thread_list) { @@ -311,7 +311,7 @@ static void DebugThreadQueue() { } for (auto& t : thread_list) { - s32 priority = ready_queue.contains(t.get()); + u32 priority = ready_queue.contains(t.get()); if (priority != -1) { LOG_DEBUG(Kernel, "0x%02X %u", priority, t->GetObjectId()); } @@ -422,7 +422,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, return ERR_OUT_OF_MEMORY; } - u32 offset = linheap_memory->size(); + size_t offset = linheap_memory->size(); // Allocate some memory from the end of the linear heap for this region. linheap_memory->insert(linheap_memory->end(), Memory::PAGE_SIZE, 0); @@ -430,7 +430,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, owner_process->linear_heap_used += Memory::PAGE_SIZE; tls_slots.emplace_back(0); // The page is completely available at the start - available_page = tls_slots.size() - 1; + available_page = static_cast(tls_slots.size() - 1); available_slot = 0; // Use the first slot in the new page auto& vm_manager = owner_process->vm_manager; @@ -457,7 +457,7 @@ ResultVal> Thread::Create(std::string name, VAddr entry_point, return MakeResult>(std::move(thread)); } -void Thread::SetPriority(s32 priority) { +void Thread::SetPriority(u32 priority) { ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST, "Invalid priority value."); // If thread was ready, adjust queues @@ -470,7 +470,7 @@ void Thread::SetPriority(s32 priority) { } void Thread::UpdatePriority() { - s32 best_priority = nominal_priority; + u32 best_priority = nominal_priority; for (auto& mutex : held_mutexes) { if (mutex->priority < best_priority) best_priority = mutex->priority; @@ -478,7 +478,7 @@ void Thread::UpdatePriority() { BoostPriority(best_priority); } -void Thread::BoostPriority(s32 priority) { +void Thread::BoostPriority(u32 priority) { // If thread was ready, adjust queues if (status == THREADSTATUS_READY) ready_queue.move(this, current_priority, priority); @@ -487,7 +487,7 @@ void Thread::BoostPriority(s32 priority) { current_priority = priority; } -SharedPtr SetupMainThread(u32 entry_point, s32 priority, SharedPtr owner_process) { +SharedPtr SetupMainThread(u32 entry_point, u32 priority, SharedPtr owner_process) { // Initialize new "main" thread auto thread_res = Thread::Create("main", entry_point, priority, 0, THREADPROCESSORID_0, Memory::HEAP_VADDR_END, owner_process); @@ -531,7 +531,7 @@ void Thread::SetWaitSynchronizationOutput(s32 output) { s32 Thread::GetWaitObjectIndex(WaitObject* object) const { ASSERT_MSG(!wait_objects.empty(), "Thread is not waiting for anything"); auto match = std::find(wait_objects.rbegin(), wait_objects.rend(), object); - return std::distance(match, wait_objects.rend()) - 1; + return static_cast(std::distance(match, wait_objects.rend()) - 1); } //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index ddc0d15c5..f02e1d43a 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -15,7 +15,7 @@ #include "core/hle/kernel/wait_object.h" #include "core/hle/result.h" -enum ThreadPriority : s32 { +enum ThreadPriority : u32 { THREADPRIO_HIGHEST = 0, ///< Highest thread priority THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps THREADPRIO_DEFAULT = 48, ///< Default thread priority for userland apps @@ -82,7 +82,7 @@ public: * Gets the thread's current priority * @return The current thread's priority */ - s32 GetPriority() const { + u32 GetPriority() const { return current_priority; } @@ -90,7 +90,7 @@ public: * Sets the thread's current priority * @param priority The new priority */ - void SetPriority(s32 priority); + void SetPriority(u32 priority); /** * Boost's a thread's priority to the best priority among the thread's held mutexes. @@ -102,7 +102,7 @@ public: * Temporarily boosts the thread's priority until the next time it is scheduled * @param priority The new priority */ - void BoostPriority(s32 priority); + void BoostPriority(u32 priority); /** * Gets the thread's thread ID @@ -176,8 +176,8 @@ public: u32 entry_point; u32 stack_top; - s32 nominal_priority; ///< Nominal thread priority, as set by the emulated application - s32 current_priority; ///< Current thread priority, can be temporarily changed + u32 nominal_priority; ///< Nominal thread priority, as set by the emulated application + u32 current_priority; ///< Current thread priority, can be temporarily changed u64 last_running_ticks; ///< CPU tick when thread was last running @@ -219,7 +219,7 @@ private: * @param owner_process The parent process for the main thread * @return A shared pointer to the main thread */ -SharedPtr SetupMainThread(u32 entry_point, s32 priority, SharedPtr owner_process); +SharedPtr SetupMainThread(u32 entry_point, u32 priority, SharedPtr owner_process); /** * Returns whether there are any threads that are ready to run. diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp index f245eda6c..56fdd977f 100644 --- a/src/core/hle/kernel/wait_object.cpp +++ b/src/core/hle/kernel/wait_object.cpp @@ -34,7 +34,7 @@ void WaitObject::RemoveWaitingThread(Thread* thread) { SharedPtr WaitObject::GetHighestPriorityReadyThread() { Thread* candidate = nullptr; - s32 candidate_priority = THREADPRIO_LOWEST + 1; + u32 candidate_priority = THREADPRIO_LOWEST + 1; for (const auto& thread : waiting_threads) { // The list of waiting threads must not contain threads that are not waiting to be awakened. diff --git a/src/core/hle/service/apt/apt.cpp b/src/core/hle/service/apt/apt.cpp index 8c0ba73f2..4c6156345 100644 --- a/src/core/hle/service/apt/apt.cpp +++ b/src/core/hle/service/apt/apt.cpp @@ -561,7 +561,7 @@ void ReceiveParameter(Service::Interface* self) { ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() : 0); - rb.PushStaticBuffer(buffer, static_cast(next_parameter->buffer.size()), 0); + rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0); Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); @@ -609,7 +609,7 @@ void GlanceParameter(Service::Interface* self) { ? Kernel::g_handle_table.Create(next_parameter->object).Unwrap() : 0); - rb.PushStaticBuffer(buffer, static_cast(next_parameter->buffer.size()), 0); + rb.PushStaticBuffer(buffer, next_parameter->buffer.size(), 0); Memory::WriteBlock(buffer, next_parameter->buffer.data(), next_parameter->buffer.size()); diff --git a/src/core/hle/service/cam/cam.cpp b/src/core/hle/service/cam/cam.cpp index c9f9e9d95..8172edae8 100644 --- a/src/core/hle/service/cam/cam.cpp +++ b/src/core/hle/service/cam/cam.cpp @@ -177,7 +177,7 @@ void CompletionEventCallBack(u64 port_id, int) { LOG_ERROR(Service_CAM, "The destination size (%u) doesn't match the source (%zu)!", port.dest_size, buffer_size); } - Memory::WriteBlock(port.dest, buffer.data(), std::min(port.dest_size, buffer_size)); + Memory::WriteBlock(port.dest, buffer.data(), std::min(port.dest_size, buffer_size)); } port.is_receiving = false; diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index f26a1f65f..f78c25fb2 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -141,7 +141,7 @@ void GetCountryCodeString(Service::Interface* self) { void GetCountryCodeID(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); - u16 country_code = cmd_buff[1]; + u16 country_code = static_cast(cmd_buff[1]); u16 country_code_id = 0; // The following algorithm will fail if the first country code isn't 0. diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 4ccb3cd32..4ee7df73c 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -217,7 +217,7 @@ void Directory::HandleSyncRequest(Kernel::SharedPtr serve LOG_TRACE(Service_FS, "Read %s: count=%d", GetName().c_str(), count); // Number of entries actually read - u32 read = backend->Read(entries.size(), entries.data()); + u32 read = backend->Read(static_cast(entries.size()), entries.data()); cmd_buff[2] = read; Memory::WriteBlock(address, entries.data(), read * sizeof(FileSys::Entry)); break; diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index aa5d821f9..379fbd71c 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -251,7 +251,7 @@ static void UpdateGyroscopeCallback(u64 userdata, int cycles_late) { Math::Vec3 gyro; std::tie(std::ignore, gyro) = motion_device->GetStatus(); double stretch = Core::System::GetInstance().perf_stats.GetLastFrameTimeScale(); - gyro *= gyroscope_coef * stretch; + gyro *= gyroscope_coef * static_cast(stretch); gyroscope_entry.x = static_cast(gyro.x); gyroscope_entry.y = static_cast(gyro.y); gyroscope_entry.z = static_cast(gyro.z); diff --git a/src/core/hle/service/ldr_ro/cro_helper.h b/src/core/hle/service/ldr_ro/cro_helper.h index 3bc10dbdc..57b4fb6df 100644 --- a/src/core/hle/service/ldr_ro/cro_helper.h +++ b/src/core/hle/service/ldr_ro/cro_helper.h @@ -413,7 +413,8 @@ private: */ template void GetEntry(std::size_t index, T& data) const { - Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); + Memory::ReadBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast(index * sizeof(T)), + &data, sizeof(T)); } /** @@ -425,7 +426,8 @@ private: */ template void SetEntry(std::size_t index, const T& data) { - Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + index * sizeof(T), &data, sizeof(T)); + Memory::WriteBlock(GetField(T::TABLE_OFFSET_FIELD) + static_cast(index * sizeof(T)), + &data, sizeof(T)); } /** diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 4e2af9ae6..8ef0cda09 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -316,7 +316,7 @@ static void RecvBeaconBroadcastData(Interface* self) { auto beacons = GetReceivedBeacons(mac_address); BeaconDataReplyHeader data_reply_header{}; - data_reply_header.total_entries = beacons.size(); + data_reply_header.total_entries = static_cast(beacons.size()); data_reply_header.max_output_size = out_buffer_size; Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); @@ -326,8 +326,8 @@ static void RecvBeaconBroadcastData(Interface* self) { for (const auto& beacon : beacons) { BeaconEntryHeader entry{}; // TODO(Subv): Figure out what this size is used for. - entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size(); - entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size(); + entry.unk_size = static_cast(sizeof(BeaconEntryHeader) + beacon.data.size()); + entry.total_size = static_cast(sizeof(BeaconEntryHeader) + beacon.data.size()); entry.wifi_channel = beacon.channel; entry.header_size = sizeof(BeaconEntryHeader); entry.mac_address = beacon.transmitter_address; @@ -338,9 +338,9 @@ static void RecvBeaconBroadcastData(Interface* self) { current_buffer_pos += sizeof(BeaconEntryHeader); Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); - current_buffer_pos += beacon.data.size(); + current_buffer_pos += static_cast(beacon.data.size()); - total_size += sizeof(BeaconEntryHeader) + beacon.data.size(); + total_size += static_cast(sizeof(BeaconEntryHeader) + beacon.data.size()); } // Update the total size in the structure and write it to the buffer again. diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp index 552eaf65e..73a80d940 100644 --- a/src/core/hle/service/nwm/uds_beacon.cpp +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -243,7 +243,7 @@ std::vector GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network EncryptedDataTag tag{}; tag.header.tag_id = static_cast(TagId::VendorSpecific); - tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size; + tag.header.length = static_cast(sizeof(tag) - sizeof(TagHeader) + payload_size); tag.oui_type = static_cast(NintendoTagId::EncryptedData0); tag.oui = NintendoOUI; @@ -279,7 +279,7 @@ std::vector GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& networ EncryptedDataTag tag{}; tag.header.tag_id = static_cast(TagId::VendorSpecific); - tag.header.length = tag_length; + tag.header.length = static_cast(tag_length); tag.oui_type = static_cast(NintendoTagId::EncryptedData1); tag.oui = NintendoOUI; diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 0fd9b8b8c..3ef2a84b6 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -197,7 +197,7 @@ static std::vector DecryptDataFrame(const std::vector& encrypted_payload df.ChannelMessageEnd(CryptoPP::DEFAULT_CHANNEL); df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); - int size = df.MaxRetrievable(); + size_t size = df.MaxRetrievable(); std::vector pdata(size); df.Get(pdata.data(), size); @@ -251,7 +251,7 @@ static std::vector EncryptDataFrame(const std::vector& payload, df.SetRetrievalChannel(CryptoPP::DEFAULT_CHANNEL); - int size = df.MaxRetrievable(); + size_t size = df.MaxRetrievable(); std::vector cipher(size); df.Get(cipher.data(), size); @@ -266,8 +266,8 @@ static std::vector EncryptDataFrame(const std::vector& payload, std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 dest_node, u16 src_node, u16 sequence_number) { std::vector buffer = GenerateLLCHeader(EtherType::SecureData); - std::vector securedata_header = - GenerateSecureDataHeader(data.size(), channel, dest_node, src_node, sequence_number); + std::vector securedata_header = GenerateSecureDataHeader( + static_cast(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()); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 05c6897bf..41c82c922 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -361,7 +361,7 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); object->Acquire(thread); - *out = std::distance(objects.begin(), itr); + *out = static_cast(std::distance(objects.begin(), itr)); return RESULT_SUCCESS; } @@ -469,7 +469,7 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl // We found a ready object, acquire it and set the result value Kernel::WaitObject* object = itr->get(); object->Acquire(thread); - *index = std::distance(objects.begin(), itr); + *index = static_cast(std::distance(objects.begin(), itr)); if (object->GetHandleType() == Kernel::HandleType::ServerSession) { auto server_session = static_cast(object); @@ -683,7 +683,7 @@ static void ExitThread() { } /// Gets the priority for the specified thread -static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { +static ResultCode GetThreadPriority(u32* priority, Kernel::Handle handle) { const SharedPtr thread = Kernel::g_handle_table.Get(handle); if (thread == nullptr) return ERR_INVALID_HANDLE; @@ -693,7 +693,7 @@ static ResultCode GetThreadPriority(s32* priority, Kernel::Handle handle) { } /// Sets the priority for the specified thread -static ResultCode SetThreadPriority(Kernel::Handle handle, s32 priority) { +static ResultCode SetThreadPriority(Kernel::Handle handle, u32 priority) { if (priority > THREADPRIO_LOWEST) { return Kernel::ERR_OUT_OF_RANGE; } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 5ea0694a9..847e69710 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -477,7 +477,7 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { while (remaining_size > 0) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; + const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); switch (current_page_table->attributes[page_index]) { case PageType::Unmapped: { @@ -500,13 +500,15 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { break; } case PageType::RasterizerCachedMemory: { - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Flush); std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount); break; } case PageType::RasterizerCachedSpecial: { DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Flush); GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); break; } @@ -544,7 +546,7 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size while (remaining_size > 0) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; + const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); switch (current_page_table->attributes[page_index]) { case PageType::Unmapped: { @@ -567,13 +569,15 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size break; } case PageType::RasterizerCachedMemory: { - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::FlushAndInvalidate); std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount); break; } case PageType::RasterizerCachedSpecial: { DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::FlushAndInvalidate); GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); break; } @@ -597,7 +601,7 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) { while (remaining_size > 0) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; + const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); switch (current_page_table->attributes[page_index]) { case PageType::Unmapped: { @@ -619,13 +623,15 @@ void ZeroBlock(const VAddr dest_addr, const size_t size) { break; } case PageType::RasterizerCachedMemory: { - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::FlushAndInvalidate); std::memset(GetPointerFromVMA(current_vaddr), 0, copy_amount); break; } case PageType::RasterizerCachedSpecial: { DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::FlushAndInvalidate); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::FlushAndInvalidate); GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, zeros.data(), copy_amount); break; } @@ -646,7 +652,7 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { while (remaining_size > 0) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); - const VAddr current_vaddr = (page_index << PAGE_BITS) + page_offset; + const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); switch (current_page_table->attributes[page_index]) { case PageType::Unmapped: { @@ -670,13 +676,15 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { break; } case PageType::RasterizerCachedMemory: { - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Flush); WriteBlock(dest_addr, GetPointerFromVMA(current_vaddr), copy_amount); break; } case PageType::RasterizerCachedSpecial: { DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - RasterizerFlushVirtualRegion(current_vaddr, copy_amount, FlushMode::Flush); + RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), + FlushMode::Flush); std::vector buffer(copy_amount); GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, buffer.data(), buffer.size()); @@ -689,8 +697,8 @@ void CopyBlock(VAddr dest_addr, VAddr src_addr, const size_t size) { page_index++; page_offset = 0; - dest_addr += copy_amount; - src_addr += copy_amount; + dest_addr += static_cast(copy_amount); + src_addr += static_cast(copy_amount); remaining_size -= copy_amount; } } -- cgit v1.2.3 From afb1012bcd7e7aea2428aadb195b04ef72fcf861 Mon Sep 17 00:00:00 2001 From: B3n30 Date: Sat, 30 Sep 2017 18:18:45 +0200 Subject: Services/UDS: Handle the rest of the connection sequence. (#2963) Services/UDS: Handle the rest of the connection sequence. --- src/core/hle/service/nwm/nwm_uds.cpp | 121 ++++++++++++++++++++++++++++++---- src/core/hle/service/nwm/uds_data.cpp | 80 +++++++++++++++++++++- src/core/hle/service/nwm/uds_data.h | 68 +++++++++++++++++-- 3 files changed, 250 insertions(+), 19 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 8ef0cda09..0aa63cc1e 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/ipc_helpers.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/lock.h" #include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" #include "core/hle/service/nwm/uds_beacon.h" @@ -100,6 +101,20 @@ void SendPacket(Network::WifiPacket& packet) { // TODO(Subv): Implement. } +/* + * Returns an available index in the nodes array for the + * currently-hosted UDS network. + */ +static u16 GetNextAvailableNodeId() { + for (u16 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"); +} + // Inserts the received beacon frame in the beacon queue and removes any older beacons if the size // limit is exceeded. void HandleBeaconFrame(const Network::WifiPacket& packet) { @@ -143,18 +158,88 @@ void HandleAssociationResponseFrame(const Network::WifiPacket& packet) { SendPacket(eapol_start); } -/* - * Returns an available index in the nodes array for the - * currently-hosted UDS network. - */ -static u16 GetNextAvailableNodeId() { - for (u16 index = 0; index < connection_status.max_nodes; ++index) { - if ((connection_status.node_bitmask & (1 << index)) == 0) - return index; - } +static void HandleEAPoLPacket(const Network::WifiPacket& packet) { + std::lock_guard lock(connection_status_mutex); - // Any connection attempts to an already full network should have been refused. - ASSERT_MSG(false, "No available connection slots in the network"); + if (GetEAPoLFrameType(packet.data) == EAPoLStartMagic) { + if (connection_status.status != static_cast(NetworkStatus::ConnectedAsHost)) { + LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + + auto node = DeserializeNodeInfoFromFrame(packet.data); + + if (connection_status.max_nodes == connection_status.total_nodes) { + // Reject connection attempt + LOG_ERROR(Service_NWM, "Reached maximum nodes, but reject packet wasn't sent."); + // TODO(B3N30): Figure out what packet is sent here + return; + } + + // Get an unused network node id + u16 node_id = GetNextAvailableNodeId(); + node.network_node_id = node_id + 1; + + connection_status.node_bitmask |= 1 << node_id; + connection_status.changed_nodes |= 1 << node_id; + connection_status.nodes[node_id] = node.network_node_id; + connection_status.total_nodes++; + + u8 current_nodes = network_info.total_nodes; + node_info[current_nodes] = node; + + network_info.total_nodes++; + + // Send the EAPoL-Logoff packet. + using Network::WifiPacket; + WifiPacket eapol_logoff; + eapol_logoff.channel = network_channel; + eapol_logoff.data = + GenerateEAPoLLogoffFrame(packet.transmitter_address, node.network_node_id, node_info, + network_info.max_nodes, network_info.total_nodes); + // TODO(Subv): Encrypt the packet. + eapol_logoff.destination_address = packet.transmitter_address; + eapol_logoff.type = WifiPacket::PacketType::Data; + + SendPacket(eapol_logoff); + // TODO(B3N30): Broadcast updated node list + // The 3ds does this presumably to support spectators. + std::lock_guard lock(HLE::g_hle_lock); + connection_status_event->Signal(); + } else { + if (connection_status.status != static_cast(NetworkStatus::NotConnected)) { + LOG_DEBUG(Service_NWM, "Connection sequence aborted, because connection status is %u", + connection_status.status); + return; + } + auto logoff = ParseEAPoLLogoffFrame(packet.data); + + network_info.total_nodes = logoff.connected_nodes; + network_info.max_nodes = logoff.max_nodes; + + connection_status.network_node_id = logoff.assigned_node_id; + connection_status.total_nodes = logoff.connected_nodes; + connection_status.max_nodes = logoff.max_nodes; + + node_info.clear(); + node_info.reserve(network_info.max_nodes); + for (size_t index = 0; index < logoff.connected_nodes; ++index) { + connection_status.node_bitmask |= 1 << index; + connection_status.changed_nodes |= 1 << index; + connection_status.nodes[index] = logoff.nodes[index].network_node_id; + + node_info.emplace_back(DeserializeNodeInfo(logoff.nodes[index])); + } + + // We're now connected, signal the application + connection_status.status = static_cast(NetworkStatus::ConnectedAsClient); + // Some games require ConnectToNetwork to block, for now it doesn't + // If blocking is implemented this lock needs to be changed, + // otherwise it might cause deadlocks + std::lock_guard lock(HLE::g_hle_lock); + connection_status_event->Signal(); + } } /* @@ -238,6 +323,17 @@ void HandleAuthenticationFrame(const Network::WifiPacket& packet) { } } +static void HandleDataFrame(const Network::WifiPacket& packet) { + switch (GetFrameEtherType(packet.data)) { + case EtherType::EAPoL: + HandleEAPoLPacket(packet); + break; + case EtherType::SecureData: + // TODO(B3N30): Handle SecureData packets + break; + } +} + /// Callback to parse and handle a received wifi packet. void OnWifiPacketReceived(const Network::WifiPacket& packet) { switch (packet.type) { @@ -250,6 +346,9 @@ void OnWifiPacketReceived(const Network::WifiPacket& packet) { case Network::WifiPacket::PacketType::AssociationResponse: HandleAssociationResponseFrame(packet); break; + case Network::WifiPacket::PacketType::Data: + HandleDataFrame(packet); + break; } } diff --git a/src/core/hle/service/nwm/uds_data.cpp b/src/core/hle/service/nwm/uds_data.cpp index 3ef2a84b6..4b389710f 100644 --- a/src/core/hle/service/nwm/uds_data.cpp +++ b/src/core/hle/service/nwm/uds_data.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include #include #include @@ -277,10 +278,10 @@ std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info) { EAPoLStartPacket eapol_start{}; eapol_start.association_id = association_id; - eapol_start.friend_code_seed = node_info.friend_code_seed; + eapol_start.node.friend_code_seed = node_info.friend_code_seed; - for (int i = 0; i < node_info.username.size(); ++i) - eapol_start.username[i] = node_info.username[i]; + std::copy(node_info.username.begin(), node_info.username.end(), + eapol_start.node.username.begin()); // Note: The network_node_id and unknown bytes seem to be uninitialized in the NWM module. // TODO(B3N30): The last 8 bytes seem to have a fixed value of 07 88 15 00 04 e9 13 00 in @@ -295,5 +296,78 @@ std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node return buffer; } +EtherType GetFrameEtherType(const std::vector& frame) { + LLCHeader header; + std::memcpy(&header, frame.data(), sizeof(header)); + + u16 ethertype = header.protocol; + return static_cast(ethertype); +} + +u16 GetEAPoLFrameType(const std::vector& frame) { + // Ignore the LLC header + u16_be eapol_type; + std::memcpy(&eapol_type, frame.data() + sizeof(LLCHeader), sizeof(eapol_type)); + return eapol_type; +} + +NodeInfo DeserializeNodeInfoFromFrame(const std::vector& frame) { + EAPoLStartPacket eapol_start; + + // Skip the LLC header + std::memcpy(&eapol_start, frame.data() + sizeof(LLCHeader), sizeof(eapol_start)); + + NodeInfo node{}; + node.friend_code_seed = eapol_start.node.friend_code_seed; + + std::copy(eapol_start.node.username.begin(), eapol_start.node.username.end(), + node.username.begin()); + + return node; +} + +NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node) { + NodeInfo node_info{}; + node_info.friend_code_seed = node.friend_code_seed; + node_info.network_node_id = node.network_node_id; + + std::copy(node.username.begin(), node.username.end(), node_info.username.begin()); + + return node_info; +} + +std::vector GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, + const NodeList& nodes, u8 max_nodes, u8 total_nodes) { + EAPoLLogoffPacket eapol_logoff{}; + eapol_logoff.assigned_node_id = network_node_id; + eapol_logoff.connected_nodes = total_nodes; + eapol_logoff.max_nodes = max_nodes; + + for (size_t index = 0; index < total_nodes; ++index) { + const auto& node_info = nodes[index]; + auto& node = eapol_logoff.nodes[index]; + + node.friend_code_seed = node_info.friend_code_seed; + node.network_node_id = node_info.network_node_id; + + std::copy(node_info.username.begin(), node_info.username.end(), node.username.begin()); + } + + std::vector eapol_buffer(sizeof(EAPoLLogoffPacket)); + std::memcpy(eapol_buffer.data(), &eapol_logoff, sizeof(eapol_logoff)); + + std::vector buffer = GenerateLLCHeader(EtherType::EAPoL); + buffer.insert(buffer.end(), eapol_buffer.begin(), eapol_buffer.end()); + return buffer; +} + +EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector& frame) { + EAPoLLogoffPacket eapol_logoff; + + // Skip the LLC header + std::memcpy(&eapol_logoff, frame.data() + sizeof(LLCHeader), sizeof(eapol_logoff)); + return eapol_logoff; +} + } // namespace NWM } // namespace Service diff --git a/src/core/hle/service/nwm/uds_data.h b/src/core/hle/service/nwm/uds_data.h index 76e8f546b..76bccb1bf 100644 --- a/src/core/hle/service/nwm/uds_data.h +++ b/src/core/hle/service/nwm/uds_data.h @@ -8,6 +8,7 @@ #include #include "common/common_types.h" #include "common/swap.h" +#include "core/hle/service/nwm/uds_beacon.h" #include "core/hle/service/service.h" namespace Service { @@ -67,6 +68,16 @@ struct DataFrameCryptoCTR { static_assert(sizeof(DataFrameCryptoCTR) == 16, "DataFrameCryptoCTR has the wrong size"); +struct EAPoLNodeInfo { + u64_be friend_code_seed; + std::array username; + INSERT_PADDING_BYTES(4); + u16_be network_node_id; + INSERT_PADDING_BYTES(6); +}; + +static_assert(sizeof(EAPoLNodeInfo) == 0x28, "EAPoLNodeInfo has the wrong size"); + constexpr u16 EAPoLStartMagic = 0x201; /* @@ -78,15 +89,27 @@ struct EAPoLStartPacket { // This value is hardcoded to 1 in the NWM module. u16_be unknown = 1; INSERT_PADDING_BYTES(2); + EAPoLNodeInfo node; +}; - u64_be friend_code_seed; - std::array username; - INSERT_PADDING_BYTES(4); - u16_be network_node_id; +static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); + +constexpr u16 EAPoLLogoffMagic = 0x202; + +struct EAPoLLogoffPacket { + u16_be magic = EAPoLLogoffMagic; + INSERT_PADDING_BYTES(2); + u16_be assigned_node_id; + MacAddress client_mac_address; INSERT_PADDING_BYTES(6); + u8 connected_nodes; + u8 max_nodes; + INSERT_PADDING_BYTES(4); + + std::array nodes; }; -static_assert(sizeof(EAPoLStartPacket) == 0x30, "EAPoLStartPacket has the wrong size"); +static_assert(sizeof(EAPoLLogoffPacket) == 0x298, "EAPoLLogoffPacket has the wrong size"); /** * Generates an unencrypted 802.11 data payload. @@ -102,5 +125,40 @@ std::vector GenerateDataPayload(const std::vector& data, u8 channel, u16 */ std::vector GenerateEAPoLStartFrame(u16 association_id, const NodeInfo& node_info); +/* + * Returns the EtherType of the specified 802.11 frame. + */ +EtherType GetFrameEtherType(const std::vector& frame); + +/* + * Returns the EAPoL type (Start / Logoff) of the specified 802.11 frame. + * Note: The frame *must* be an EAPoL frame. + */ +u16 GetEAPoLFrameType(const std::vector& frame); + +/* + * Returns a deserialized NodeInfo structure from the information inside an EAPoL-Start packet + * encapsulated in an 802.11 data frame. + */ +NodeInfo DeserializeNodeInfoFromFrame(const std::vector& frame); + +/* + * Returns a NodeInfo constructed from the data in the specified EAPoLNodeInfo. + */ +NodeInfo DeserializeNodeInfo(const EAPoLNodeInfo& node); + +/* + * Generates an unencrypted 802.11 data frame body with the EAPoL-Logoff format for UDS + * communication. + * @returns The generated frame body. + */ +std::vector GenerateEAPoLLogoffFrame(const MacAddress& mac_address, u16 network_node_id, + const NodeList& nodes, u8 max_nodes, u8 total_nodes); + +/* + * Returns a EAPoLLogoffPacket representing the specified 802.11-encapsulated data frame. + */ +EAPoLLogoffPacket ParseEAPoLLogoffFrame(const std::vector& frame); + } // namespace NWM } // namespace Service -- cgit v1.2.3 From 529f4a01318a450f999ffa7e01c5c26f801d22e0 Mon Sep 17 00:00:00 2001 From: Huw Pascoe Date: Sat, 30 Sep 2017 17:25:49 +0100 Subject: Moved down_count to CoreTiming --- src/core/arm/arm_interface.h | 9 --------- src/core/arm/dynarmic/arm_dynarmic.cpp | 9 +-------- src/core/arm/dynarmic/arm_dynarmic.h | 2 -- src/core/arm/dyncom/arm_dyncom.cpp | 8 +------- src/core/arm/dyncom/arm_dyncom.h | 2 -- src/core/core_timing.cpp | 36 ++++++++++++++++++++++------------ src/core/core_timing.h | 6 ++++++ src/core/hle/svc.cpp | 2 +- 8 files changed, 32 insertions(+), 42 deletions(-) (limited to 'src/core') diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 2aa017a54..ba528403c 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -124,12 +124,6 @@ public: */ virtual void SetCP15Register(CP15Register reg, u32 value) = 0; - /** - * Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) - * @param ticks Number of ticks to advance the CPU core - */ - virtual void AddTicks(u64 ticks) = 0; - /** * Saves the current CPU context * @param ctx Thread context to save @@ -150,9 +144,6 @@ public: return num_instructions; } - s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, - /// decreased by the cpu run loop - protected: /** * Executes the given number of instructions diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 42ae93ae8..2cb56d12f 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -124,13 +124,6 @@ void ARM_Dynarmic::SetCP15Register(CP15Register reg, u32 value) { interpreter_state->CP15[reg] = value; } -void ARM_Dynarmic::AddTicks(u64 ticks) { - down_count -= ticks; - if (down_count < 0) { - CoreTiming::Advance(); - } -} - MICROPROFILE_DEFINE(ARM_Jit, "ARM JIT", "ARM JIT", MP_RGB(255, 64, 64)); void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { @@ -139,7 +132,7 @@ void ARM_Dynarmic::ExecuteInstructions(int num_instructions) { std::size_t ticks_executed = jit->Run(static_cast(num_instructions)); - AddTicks(ticks_executed); + CoreTiming::AddTicks(ticks_executed); } void ARM_Dynarmic::SaveContext(ARM_Interface::ThreadContext& ctx) { diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index 96148a1a5..0b00158a5 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -32,8 +32,6 @@ public: u32 GetCP15Register(CP15Register reg) override; void SetCP15Register(CP15Register reg, u32 value) override; - void AddTicks(u64 ticks) override; - void SaveContext(ThreadContext& ctx) override; void LoadContext(const ThreadContext& ctx) override; diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index da955c9b9..4d72aef77 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -77,12 +77,6 @@ void ARM_DynCom::SetCP15Register(CP15Register reg, u32 value) { state->CP15[reg] = value; } -void ARM_DynCom::AddTicks(u64 ticks) { - down_count -= ticks; - if (down_count < 0) - CoreTiming::Advance(); -} - void ARM_DynCom::ExecuteInstructions(int num_instructions) { state->NumInstrsToExecute = num_instructions; @@ -90,7 +84,7 @@ void ARM_DynCom::ExecuteInstructions(int num_instructions) { // executing one instruction at a time. Otherwise, if a block is being executed, more // instructions may actually be executed than specified. unsigned ticks_executed = InterpreterMainLoop(state.get()); - AddTicks(ticks_executed); + CoreTiming::AddTicks(ticks_executed); } void ARM_DynCom::SaveContext(ThreadContext& ctx) { diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index 0ae535671..fc1ffed6a 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -31,8 +31,6 @@ public: u32 GetCP15Register(CP15Register reg) override; void SetCP15Register(CP15Register reg, u32 value) override; - void AddTicks(u64 ticks) override; - void SaveContext(ThreadContext& ctx) override; void LoadContext(const ThreadContext& ctx) override; diff --git a/src/core/core_timing.cpp b/src/core/core_timing.cpp index 276ecfdf6..5e2a5d00f 100644 --- a/src/core/core_timing.cpp +++ b/src/core/core_timing.cpp @@ -57,6 +57,9 @@ static s64 idled_cycles; static s64 last_global_time_ticks; static s64 last_global_time_us; +static s64 down_count = 0; ///< A decreasing counter of remaining cycles before the next event, + /// decreased by the cpu run loop + static std::recursive_mutex external_event_section; // Warning: not included in save state. @@ -146,7 +149,7 @@ void UnregisterAllEvents() { } void Init() { - Core::CPU().down_count = INITIAL_SLICE_LENGTH; + down_count = INITIAL_SLICE_LENGTH; g_slice_length = INITIAL_SLICE_LENGTH; global_timer = 0; idled_cycles = 0; @@ -185,8 +188,15 @@ void Shutdown() { } } +void AddTicks(u64 ticks) { + down_count -= ticks; + if (down_count < 0) { + Advance(); + } +} + u64 GetTicks() { - return (u64)global_timer + g_slice_length - Core::CPU().down_count; + return (u64)global_timer + g_slice_length - down_count; } u64 GetIdleTicks() { @@ -460,18 +470,18 @@ void MoveEvents() { } void ForceCheck() { - s64 cycles_executed = g_slice_length - Core::CPU().down_count; + s64 cycles_executed = g_slice_length - down_count; global_timer += cycles_executed; // This will cause us to check for new events immediately. - Core::CPU().down_count = 0; + down_count = 0; // But let's not eat a bunch more time in Advance() because of this. g_slice_length = 0; } void Advance() { - s64 cycles_executed = g_slice_length - Core::CPU().down_count; + s64 cycles_executed = g_slice_length - down_count; global_timer += cycles_executed; - Core::CPU().down_count = g_slice_length; + down_count = g_slice_length; if (has_ts_events) MoveEvents(); @@ -480,7 +490,7 @@ void Advance() { if (!first) { if (g_slice_length < 10000) { g_slice_length += 10000; - Core::CPU().down_count += g_slice_length; + down_count += g_slice_length; } } else { // Note that events can eat cycles as well. @@ -490,7 +500,7 @@ void Advance() { const int diff = target - g_slice_length; g_slice_length += diff; - Core::CPU().down_count += diff; + down_count += diff; } if (advance_callback) advance_callback(static_cast(cycles_executed)); @@ -506,12 +516,12 @@ void LogPendingEvents() { } void Idle(int max_idle) { - s64 cycles_down = Core::CPU().down_count; + s64 cycles_down = down_count; if (max_idle != 0 && cycles_down > max_idle) cycles_down = max_idle; if (first && cycles_down > 0) { - s64 cycles_executed = g_slice_length - Core::CPU().down_count; + s64 cycles_executed = g_slice_length - down_count; s64 cycles_next_event = first->time - global_timer; if (cycles_next_event < cycles_executed + cycles_down) { @@ -526,9 +536,9 @@ void Idle(int max_idle) { cycles_down / (float)(g_clock_rate_arm11 * 0.001f)); idled_cycles += cycles_down; - Core::CPU().down_count -= cycles_down; - if (Core::CPU().down_count == 0) - Core::CPU().down_count = -1; + down_count -= cycles_down; + if (down_count == 0) + down_count = -1; } std::string GetScheduledEventsSummary() { diff --git a/src/core/core_timing.h b/src/core/core_timing.h index d2f85cd4d..897350801 100644 --- a/src/core/core_timing.h +++ b/src/core/core_timing.h @@ -67,6 +67,12 @@ void Shutdown(); typedef void (*MHzChangeCallback)(); typedef std::function TimedCallback; +/** +* Advance the CPU core by the specified number of ticks (e.g. to simulate CPU execution time) +* @param ticks Number of ticks to advance the CPU core +*/ +void AddTicks(u64 ticks); + u64 GetTicks(); u64 GetIdleTicks(); u64 GetGlobalTimeUs(); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index fefd50805..6be5db13f 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -1039,7 +1039,7 @@ static void SleepThread(s64 nanoseconds) { static s64 GetSystemTick() { s64 result = CoreTiming::GetTicks(); // Advance time to defeat dumb games (like Cubic Ninja) that busy-wait for the frame to end. - Core::CPU().AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b + CoreTiming::AddTicks(150); // Measured time between two calls on a 9.2 o3DS with Ninjhax 1.1b return result; } -- cgit v1.2.3 From 5bae5a48b90cc9f6c847040e6f486296ed135017 Mon Sep 17 00:00:00 2001 From: Subv Date: Sat, 30 Sep 2017 13:19:58 -0500 Subject: Services/NIM: Implement CheckForSysUpdateEvent. Implementation verified by reverse engineering. This lets the Home Menu boot without crashing on startup. --- src/core/hle/service/nim/nim.cpp | 18 +++++++++++++++++- src/core/hle/service/nim/nim.h | 11 +++++++++++ src/core/hle/service/nim/nim_u.cpp | 2 +- 3 files changed, 29 insertions(+), 2 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp index d5624fe54..b10d5852b 100644 --- a/src/core/hle/service/nim/nim.cpp +++ b/src/core/hle/service/nim/nim.cpp @@ -5,6 +5,8 @@ #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/ipc.h" +#include "core/hle/ipc_helpers.h" +#include "core/hle/kernel/event.h" #include "core/hle/service/nim/nim.h" #include "core/hle/service/nim/nim_aoc.h" #include "core/hle/service/nim/nim_s.h" @@ -14,6 +16,16 @@ namespace Service { namespace NIM { +static Kernel::SharedPtr nim_system_update_event; + +void CheckForSysUpdateEvent(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); // 0x50000 + IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(nim_system_update_event).Unwrap()); + LOG_TRACE(Service_NIM, "called"); +} + void CheckSysUpdateAvailable(Service::Interface* self) { u32* cmd_buff = Kernel::GetCommandBuffer(); @@ -29,9 +41,13 @@ void Init() { AddService(new NIM_AOC_Interface); AddService(new NIM_S_Interface); AddService(new NIM_U_Interface); + + nim_system_update_event = Kernel::Event::Create(ResetType::OneShot, "NIM System Update Event"); } -void Shutdown() {} +void Shutdown() { + nim_system_update_event = nullptr; +} } // namespace NIM diff --git a/src/core/hle/service/nim/nim.h b/src/core/hle/service/nim/nim.h index c3106f18b..dbf605e5a 100644 --- a/src/core/hle/service/nim/nim.h +++ b/src/core/hle/service/nim/nim.h @@ -10,6 +10,17 @@ class Interface; namespace NIM { +/** + * NIM::CheckForSysUpdateEvent service function + * Inputs: + * 1 : None + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Copy handle descriptor + * 3 : System Update event handle + */ +void CheckForSysUpdateEvent(Service::Interface* self); + /** * NIM::CheckSysUpdateAvailable service function * Inputs: diff --git a/src/core/hle/service/nim/nim_u.cpp b/src/core/hle/service/nim/nim_u.cpp index 7664bad60..569660278 100644 --- a/src/core/hle/service/nim/nim_u.cpp +++ b/src/core/hle/service/nim/nim_u.cpp @@ -12,7 +12,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00010000, nullptr, "StartSysUpdate"}, {0x00020000, nullptr, "GetUpdateDownloadProgress"}, {0x00040000, nullptr, "FinishTitlesInstall"}, - {0x00050000, nullptr, "CheckForSysUpdateEvent"}, + {0x00050000, CheckForSysUpdateEvent, "CheckForSysUpdateEvent"}, {0x00090000, CheckSysUpdateAvailable, "CheckSysUpdateAvailable"}, {0x000A0000, nullptr, "GetState"}, {0x000B0000, nullptr, "GetSystemTitleHash"}, -- cgit v1.2.3 From c93e5ecfe68028c75e36a861fff2e287875f5794 Mon Sep 17 00:00:00 2001 From: shinyquagsire23 Date: Mon, 25 Sep 2017 22:21:39 -0600 Subject: file_sys/archive_ncch: use NCCHContainer instead of loading .romfs files --- src/core/file_sys/archive_ncch.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index 6d9007731..19e1eb981 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -13,7 +13,9 @@ #include "core/file_sys/archive_ncch.h" #include "core/file_sys/errors.h" #include "core/file_sys/ivfc_archive.h" +#include "core/file_sys/ncch_container.h" #include "core/hle/service/fs/archive.h" +#include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // FileSys namespace @@ -25,8 +27,8 @@ static std::string GetNCCHContainerPath(const std::string& nand_directory) { } static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) { - return Common::StringFromFormat("%s%08x/%08x/content/00000000.app.romfs", mount_point.c_str(), - high, low); + return Common::StringFromFormat("%s%08x/%08x/content/00000000.app", mount_point.c_str(), high, + low); } ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) @@ -38,9 +40,14 @@ ResultVal> ArchiveFactory_NCCH::Open(const Path& u32 high = data[1]; u32 low = data[0]; std::string file_path = GetNCCHPath(mount_point, high, low); - auto file = std::make_shared(file_path, "rb"); - if (!file->IsOpen()) { + std::shared_ptr romfs_file; + u64 romfs_offset = 0; + u64 romfs_size = 0; + auto ncch_container = NCCHContainer(file_path); + + if (ncch_container.ReadRomFS(romfs_file, romfs_offset, romfs_size) != + Loader::ResultStatus::Success) { // High Title ID of the archive: The category (https://3dbrew.org/wiki/Title_list). constexpr u32 shared_data_archive = 0x0004009B; constexpr u32 system_data_archive = 0x000400DB; @@ -74,9 +81,8 @@ ResultVal> ArchiveFactory_NCCH::Open(const Path& } return ERROR_NOT_FOUND; } - auto size = file->GetSize(); - auto archive = std::make_unique(file, 0, size); + auto archive = std::make_unique(romfs_file, romfs_offset, romfs_size); return MakeResult>(std::move(archive)); } -- cgit v1.2.3 From e21f2348e7da4ba2de9fe287276e8c215bcfe9d0 Mon Sep 17 00:00:00 2001 From: shinyquagsire23 Date: Sun, 1 Oct 2017 10:30:47 -0600 Subject: file_sys/ncch_container: add RomFS, ExeFS override to allow for backward compatibility with existing .romfs system archive dumps --- src/core/file_sys/ncch_container.cpp | 245 +++++++++++++++++++++++++---------- src/core/file_sys/ncch_container.h | 30 +++++ 2 files changed, 206 insertions(+), 69 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/ncch_container.cpp b/src/core/file_sys/ncch_container.cpp index 59c72f3e9..b9fb940c7 100644 --- a/src/core/file_sys/ncch_container.cpp +++ b/src/core/file_sys/ncch_container.cpp @@ -116,92 +116,143 @@ Loader::ResultStatus NCCHContainer::Load() { if (is_loaded) return Loader::ResultStatus::Success; - // Reset read pointer in case this file has been read before. - file.Seek(0, SEEK_SET); + if (file.IsOpen()) { + // Reset read pointer in case this file has been read before. + file.Seek(0, SEEK_SET); - if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) - return Loader::ResultStatus::Error; + if (file.ReadBytes(&ncch_header, sizeof(NCCH_Header)) != sizeof(NCCH_Header)) + return Loader::ResultStatus::Error; - // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... - if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { - LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!"); - ncch_offset = 0x4000; - file.Seek(ncch_offset, SEEK_SET); - file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); - } + // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... + if (Loader::MakeMagic('N', 'C', 'S', 'D') == ncch_header.magic) { + LOG_DEBUG(Service_FS, "Only loading the first (bootable) NCCH within the NCSD file!"); + ncch_offset = 0x4000; + file.Seek(ncch_offset, SEEK_SET); + file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); + } - // Verify we are loading the correct file type... - if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) - return Loader::ResultStatus::ErrorInvalidFormat; + // Verify we are loading the correct file type... + if (Loader::MakeMagic('N', 'C', 'C', 'H') != ncch_header.magic) + return Loader::ResultStatus::ErrorInvalidFormat; + + has_header = true; + + // System archives and DLC don't have an extended header but have RomFS + if (ncch_header.extended_header_size) { + if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != + sizeof(ExHeader_Header)) + return Loader::ResultStatus::Error; + + is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; + u32 entry_point = exheader_header.codeset_info.text.address; + u32 code_size = exheader_header.codeset_info.text.code_size; + u32 stack_size = exheader_header.codeset_info.stack_size; + u32 bss_size = exheader_header.codeset_info.bss_size; + u32 core_version = exheader_header.arm11_system_local_caps.core_version; + u8 priority = exheader_header.arm11_system_local_caps.priority; + u8 resource_limit_category = + exheader_header.arm11_system_local_caps.resource_limit_category; + + LOG_DEBUG(Service_FS, "Name: %s", + exheader_header.codeset_info.name); + LOG_DEBUG(Service_FS, "Program ID: %016" PRIX64, + ncch_header.program_id); + LOG_DEBUG(Service_FS, "Code compressed: %s", is_compressed ? "yes" : "no"); + LOG_DEBUG(Service_FS, "Entry point: 0x%08X", entry_point); + LOG_DEBUG(Service_FS, "Code size: 0x%08X", code_size); + LOG_DEBUG(Service_FS, "Stack size: 0x%08X", stack_size); + LOG_DEBUG(Service_FS, "Bss size: 0x%08X", bss_size); + LOG_DEBUG(Service_FS, "Core version: %d", core_version); + LOG_DEBUG(Service_FS, "Thread priority: 0x%X", priority); + LOG_DEBUG(Service_FS, "Resource limit category: %d", resource_limit_category); + LOG_DEBUG(Service_FS, "System Mode: %d", + static_cast(exheader_header.arm11_system_local_caps.system_mode)); + + if (exheader_header.system_info.jump_id != ncch_header.program_id) { + LOG_ERROR(Service_FS, + "ExHeader Program ID mismatch: the ROM is probably encrypted."); + return Loader::ResultStatus::ErrorEncrypted; + } - // System archives and DLC don't have an extended header but have RomFS - if (ncch_header.extended_header_size) { - if (file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)) != sizeof(ExHeader_Header)) - return Loader::ResultStatus::Error; + has_exheader = true; + } - is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; - u32 entry_point = exheader_header.codeset_info.text.address; - u32 code_size = exheader_header.codeset_info.text.code_size; - u32 stack_size = exheader_header.codeset_info.stack_size; - u32 bss_size = exheader_header.codeset_info.bss_size; - u32 core_version = exheader_header.arm11_system_local_caps.core_version; - u8 priority = exheader_header.arm11_system_local_caps.priority; - u8 resource_limit_category = - exheader_header.arm11_system_local_caps.resource_limit_category; - - LOG_DEBUG(Service_FS, "Name: %s", exheader_header.codeset_info.name); - LOG_DEBUG(Service_FS, "Program ID: %016" PRIX64, ncch_header.program_id); - LOG_DEBUG(Service_FS, "Code compressed: %s", is_compressed ? "yes" : "no"); - LOG_DEBUG(Service_FS, "Entry point: 0x%08X", entry_point); - LOG_DEBUG(Service_FS, "Code size: 0x%08X", code_size); - LOG_DEBUG(Service_FS, "Stack size: 0x%08X", stack_size); - LOG_DEBUG(Service_FS, "Bss size: 0x%08X", bss_size); - LOG_DEBUG(Service_FS, "Core version: %d", core_version); - LOG_DEBUG(Service_FS, "Thread priority: 0x%X", priority); - LOG_DEBUG(Service_FS, "Resource limit category: %d", resource_limit_category); - LOG_DEBUG(Service_FS, "System Mode: %d", - static_cast(exheader_header.arm11_system_local_caps.system_mode)); - - if (exheader_header.system_info.jump_id != ncch_header.program_id) { - LOG_ERROR(Service_FS, "ExHeader Program ID mismatch: the ROM is probably encrypted."); - return Loader::ResultStatus::ErrorEncrypted; + // DLC can have an ExeFS and a RomFS but no extended header + if (ncch_header.exefs_size) { + exefs_offset = ncch_header.exefs_offset * kBlockSize; + u32 exefs_size = ncch_header.exefs_size * kBlockSize; + + LOG_DEBUG(Service_FS, "ExeFS offset: 0x%08X", exefs_offset); + LOG_DEBUG(Service_FS, "ExeFS size: 0x%08X", exefs_size); + + file.Seek(exefs_offset + ncch_offset, SEEK_SET); + if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) + return Loader::ResultStatus::Error; + + exefs_file = FileUtil::IOFile(filepath, "rb"); + has_exefs = true; } - has_exheader = true; + if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) + has_romfs = true; } - // DLC can have an ExeFS and a RomFS but no extended header - if (ncch_header.exefs_size) { - exefs_offset = ncch_header.exefs_offset * kBlockSize; - u32 exefs_size = ncch_header.exefs_size * kBlockSize; + LoadOverrides(); - LOG_DEBUG(Service_FS, "ExeFS offset: 0x%08X", exefs_offset); - LOG_DEBUG(Service_FS, "ExeFS size: 0x%08X", exefs_size); + // We need at least one of these or overrides, practically + if (!(has_exefs || has_romfs || is_tainted)) + return Loader::ResultStatus::Error; - file.Seek(exefs_offset + ncch_offset, SEEK_SET); - if (file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) != sizeof(ExeFs_Header)) - return Loader::ResultStatus::Error; + is_loaded = true; + return Loader::ResultStatus::Success; +} - has_exefs = true; +Loader::ResultStatus NCCHContainer::LoadOverrides() { + // Check for split-off files, mark the archive as tainted if we will use them + std::string romfs_override = filepath + ".romfs"; + if (FileUtil::Exists(romfs_override)) { + is_tainted = true; } - if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) - has_romfs = true; + // If we have a split-off exefs file/folder, it takes priority + std::string exefs_override = filepath + ".exefs"; + std::string exefsdir_override = filepath + ".exefsdir/"; + if (FileUtil::Exists(exefs_override)) { + exefs_file = FileUtil::IOFile(exefs_override, "rb"); + + if (exefs_file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)) == sizeof(ExeFs_Header)) { + LOG_DEBUG(Service_FS, "Loading ExeFS section from %s", exefs_override.c_str()); + exefs_offset = 0; + is_tainted = true; + has_exefs = true; + } else { + exefs_file = FileUtil::IOFile(filepath, "rb"); + } + } else if (FileUtil::Exists(exefsdir_override) && FileUtil::IsDirectory(exefsdir_override)) { + is_tainted = true; + } + + if (is_tainted) + LOG_WARNING(Service_FS, + "Loaded NCCH %s is tainted, application behavior may not be as expected!", + filepath.c_str()); - is_loaded = true; return Loader::ResultStatus::Success; } Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vector& buffer) { - if (!file.IsOpen()) - return Loader::ResultStatus::Error; - Loader::ResultStatus result = Load(); if (result != Loader::ResultStatus::Success) return result; - if (!has_exefs) - return Loader::ResultStatus::ErrorNotUsed; + // Check if we have files that can drop-in and replace + result = LoadOverrideExeFSSection(name, buffer); + if (result == Loader::ResultStatus::Success || !has_exefs) + return result; + + // If we don't have any separate files, we'll need a full ExeFS + if (!exefs_file.IsOpen()) + return Loader::ResultStatus::Error; LOG_DEBUG(Service_FS, "%d sections:", kMaxSections); // Iterate through the ExeFs archive until we find a section with the specified name... @@ -215,7 +266,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect s64 section_offset = (section.offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); - file.Seek(section_offset, SEEK_SET); + exefs_file.Seek(section_offset, SEEK_SET); if (strcmp(section.name, ".code") == 0 && is_compressed) { // Section is compressed, read compressed .code section... @@ -226,7 +277,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect return Loader::ResultStatus::ErrorMemoryAllocationFailed; } - if (file.ReadBytes(&temp_buffer[0], section.size) != section.size) + if (exefs_file.ReadBytes(&temp_buffer[0], section.size) != section.size) return Loader::ResultStatus::Error; // Decompress .code section... @@ -237,7 +288,7 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect } else { // Section is uncompressed... buffer.resize(section.size); - if (file.ReadBytes(&buffer[0], section.size) != section.size) + if (exefs_file.ReadBytes(&buffer[0], section.size) != section.size) return Loader::ResultStatus::Error; } return Loader::ResultStatus::Success; @@ -246,20 +297,56 @@ Loader::ResultStatus NCCHContainer::LoadSectionExeFS(const char* name, std::vect return Loader::ResultStatus::ErrorNotUsed; } -Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romfs_file, - u64& offset, u64& size) { - if (!file.IsOpen()) +Loader::ResultStatus NCCHContainer::LoadOverrideExeFSSection(const char* name, + std::vector& buffer) { + std::string override_name; + + // Map our section name to the extracted equivalent + if (!strcmp(name, ".code")) + override_name = "code.bin"; + else if (!strcmp(name, "icon")) + override_name = "code.bin"; + else if (!strcmp(name, "banner")) + override_name = "banner.bnr"; + else if (!strcmp(name, "logo")) + override_name = "logo.bcma.lz"; + else return Loader::ResultStatus::Error; + std::string section_override = filepath + ".exefsdir/" + override_name; + FileUtil::IOFile section_file(section_override, "rb"); + + if (section_file.IsOpen()) { + auto section_size = section_file.GetSize(); + buffer.resize(section_size); + + section_file.Seek(0, SEEK_SET); + if (section_file.ReadBytes(&buffer[0], section_size) == section_size) { + LOG_WARNING(Service_FS, "File %s overriding built-in ExeFS file", + section_override.c_str()); + return Loader::ResultStatus::Success; + } + } + return Loader::ResultStatus::ErrorNotUsed; +} + +Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& romfs_file, + u64& offset, u64& size) { Loader::ResultStatus result = Load(); if (result != Loader::ResultStatus::Success) return result; + if (ReadOverrideRomFS(romfs_file, offset, size) == Loader::ResultStatus::Success) + return Loader::ResultStatus::Success; + if (!has_romfs) { LOG_DEBUG(Service_FS, "RomFS requested from NCCH which has no RomFS"); return Loader::ResultStatus::ErrorNotUsed; } + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; @@ -280,11 +367,31 @@ Loader::ResultStatus NCCHContainer::ReadRomFS(std::shared_ptr& return Loader::ResultStatus::Success; } +Loader::ResultStatus NCCHContainer::ReadOverrideRomFS(std::shared_ptr& romfs_file, + u64& offset, u64& size) { + // Check for RomFS overrides + std::string split_filepath = filepath + ".romfs"; + if (FileUtil::Exists(split_filepath)) { + romfs_file = std::make_shared(split_filepath, "rb"); + if (romfs_file->IsOpen()) { + LOG_WARNING(Service_FS, "File %s overriding built-in RomFS", split_filepath.c_str()); + offset = 0; + size = romfs_file->GetSize(); + return Loader::ResultStatus::Success; + } + } + + return Loader::ResultStatus::ErrorNotUsed; +} + Loader::ResultStatus NCCHContainer::ReadProgramId(u64_le& program_id) { Loader::ResultStatus result = Load(); if (result != Loader::ResultStatus::Success) return result; + if (!has_header) + return Loader::ResultStatus::ErrorNotUsed; + program_id = ncch_header.program_id; return Loader::ResultStatus::Success; } diff --git a/src/core/file_sys/ncch_container.h b/src/core/file_sys/ncch_container.h index 8af9032b4..2cc9d13dc 100644 --- a/src/core/file_sys/ncch_container.h +++ b/src/core/file_sys/ncch_container.h @@ -179,6 +179,13 @@ public: */ Loader::ResultStatus Load(); + /** + * Attempt to find overridden sections for the NCCH and mark the container as tainted + * if any are found. + * @return ResultStatus result of function + */ + Loader::ResultStatus LoadOverrides(); + /** * Reads an application ExeFS section of an NCCH file (e.g. .code, .logo, etc.) * @param name Name of section to read out of NCCH file @@ -187,6 +194,15 @@ public: */ Loader::ResultStatus LoadSectionExeFS(const char* name, std::vector& buffer); + /** + * Reads an application ExeFS section from external files instead of an NCCH file, + * (e.g. code.bin, logo.bcma.lz, icon.icn, banner.bnr) + * @param name Name of section to read from external files + * @param buffer Vector to read data into + * @return ResultStatus result of function + */ + Loader::ResultStatus LoadOverrideExeFSSection(const char* name, std::vector& buffer); + /** * Get the RomFS of the NCCH container * Since the RomFS can be huge, we return a file reference instead of copying to a buffer @@ -198,6 +214,17 @@ public: Loader::ResultStatus ReadRomFS(std::shared_ptr& romfs_file, u64& offset, u64& size); + /** + * Get the override RomFS of the NCCH container + * Since the RomFS can be huge, we return a file reference instead of copying to a buffer + * @param romfs_file The file containing the RomFS + * @param offset The offset the romfs begins on + * @param size The size of the romfs + * @return ResultStatus result of function + */ + Loader::ResultStatus ReadOverrideRomFS(std::shared_ptr& romfs_file, + u64& offset, u64& size); + /** * Get the Program ID of the NCCH container * @return ResultStatus result of function @@ -227,10 +254,12 @@ public: ExHeader_Header exheader_header; private: + bool has_header = false; bool has_exheader = false; bool has_exefs = false; bool has_romfs = false; + bool is_tainted = false; // Are there parts of this container being overridden? bool is_loaded = false; bool is_compressed = false; @@ -239,6 +268,7 @@ private: std::string filepath; FileUtil::IOFile file; + FileUtil::IOFile exefs_file; }; } // namespace FileSys -- cgit v1.2.3 From 8e10c9bb2e8690055ba07003ebd53a5215f82f8f Mon Sep 17 00:00:00 2001 From: shinyquagsire23 Date: Sun, 1 Oct 2017 10:32:43 -0600 Subject: file_sys: add class for Title Metadata (TMD) --- src/core/CMakeLists.txt | 1 + src/core/file_sys/title_metadata.cpp | 212 +++++++++++++++++++++++++++++++++++ src/core/file_sys/title_metadata.h | 125 +++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 src/core/file_sys/title_metadata.cpp create mode 100644 src/core/file_sys/title_metadata.h (limited to 'src/core') diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3ed619991..2618da18c 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRCS file_sys/ncch_container.cpp file_sys/path_parser.cpp file_sys/savedata_archive.cpp + file_sys/title_metadata.cpp frontend/camera/blank_camera.cpp frontend/camera/factory.cpp frontend/camera/interface.cpp diff --git a/src/core/file_sys/title_metadata.cpp b/src/core/file_sys/title_metadata.cpp new file mode 100644 index 000000000..1ef8840a0 --- /dev/null +++ b/src/core/file_sys/title_metadata.cpp @@ -0,0 +1,212 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/alignment.h" +#include "common/file_util.h" +#include "common/logging/log.h" +#include "core/file_sys/title_metadata.h" +#include "core/loader/loader.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +static u32 GetSignatureSize(u32 signature_type) { + switch (signature_type) { + case Rsa4096Sha1: + case Rsa4096Sha256: + return 0x200; + + case Rsa2048Sha1: + case Rsa2048Sha256: + return 0x100; + + case EllipticSha1: + case EcdsaSha256: + return 0x3C; + } +} + +Loader::ResultStatus TitleMetadata::Load() { + FileUtil::IOFile file(filepath, "rb"); + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + if (!file.ReadBytes(&signature_type, sizeof(u32_be))) + return Loader::ResultStatus::Error; + + // Signature lengths are variable, and the body follows the signature + u32 signature_size = GetSignatureSize(signature_type); + + tmd_signature.resize(signature_size); + if (!file.ReadBytes(&tmd_signature[0], signature_size)) + return Loader::ResultStatus::Error; + + // The TMD body start position is rounded to the nearest 0x40 after the signature + size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); + file.Seek(body_start, SEEK_SET); + + // Read our TMD body, then load the amount of ContentChunks specified + if (file.ReadBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body)) + return Loader::ResultStatus::Error; + + for (u16 i = 0; i < tmd_body.content_count; i++) { + ContentChunk chunk; + if (file.ReadBytes(&chunk, sizeof(ContentChunk)) == sizeof(ContentChunk)) { + tmd_chunks.push_back(chunk); + } else { + LOG_ERROR(Service_FS, "Malformed TMD %s, failed to load content chunk index %u!", + filepath.c_str(), i); + return Loader::ResultStatus::ErrorInvalidFormat; + } + } + + return Loader::ResultStatus::Success; +} + +Loader::ResultStatus TitleMetadata::Save() { + FileUtil::IOFile file(filepath, "wb"); + if (!file.IsOpen()) + return Loader::ResultStatus::Error; + + if (!file.WriteBytes(&signature_type, sizeof(u32_be))) + return Loader::ResultStatus::Error; + + // Signature lengths are variable, and the body follows the signature + u32 signature_size = GetSignatureSize(signature_type); + + if (!file.WriteBytes(tmd_signature.data(), signature_size)) + return Loader::ResultStatus::Error; + + // The TMD body start position is rounded to the nearest 0x40 after the signature + size_t body_start = Common::AlignUp(signature_size + sizeof(u32), 0x40); + file.Seek(body_start, SEEK_SET); + + // Update our TMD body values and hashes + tmd_body.content_count = static_cast(tmd_chunks.size()); + + // TODO(shinyquagsire23): Do TMDs with more than one contentinfo exist? + // For now we'll just adjust the first index to hold all content chunks + // and ensure that no further content info data exists. + tmd_body.contentinfo = {}; + tmd_body.contentinfo[0].index = 0; + tmd_body.contentinfo[0].command_count = static_cast(tmd_chunks.size()); + + CryptoPP::SHA256 chunk_hash; + for (u16 i = 0; i < tmd_body.content_count; i++) { + chunk_hash.Update(reinterpret_cast(&tmd_chunks[i]), sizeof(ContentChunk)); + } + chunk_hash.Final(tmd_body.contentinfo[0].hash.data()); + + CryptoPP::SHA256 contentinfo_hash; + for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { + chunk_hash.Update(reinterpret_cast(&tmd_body.contentinfo[i]), sizeof(ContentInfo)); + } + chunk_hash.Final(tmd_body.contentinfo_hash.data()); + + // Write our TMD body, then write each of our ContentChunks + if (file.WriteBytes(&tmd_body, sizeof(TitleMetadata::Body)) != sizeof(TitleMetadata::Body)) + return Loader::ResultStatus::Error; + + for (u16 i = 0; i < tmd_body.content_count; i++) { + ContentChunk chunk = tmd_chunks[i]; + if (file.WriteBytes(&chunk, sizeof(ContentChunk)) != sizeof(ContentChunk)) + return Loader::ResultStatus::Error; + } + + return Loader::ResultStatus::Success; +} + +u64 TitleMetadata::GetTitleID() const { + return tmd_body.title_id; +} + +u32 TitleMetadata::GetTitleType() const { + return tmd_body.title_type; +} + +u16 TitleMetadata::GetTitleVersion() const { + return tmd_body.title_version; +} + +u64 TitleMetadata::GetSystemVersion() const { + return tmd_body.system_version; +} + +size_t TitleMetadata::GetContentCount() const { + return tmd_chunks.size(); +} + +u32 TitleMetadata::GetBootContentID() const { + return tmd_chunks[TMDContentIndex::Main].id; +} + +u32 TitleMetadata::GetManualContentID() const { + return tmd_chunks[TMDContentIndex::Manual].id; +} + +u32 TitleMetadata::GetDLPContentID() const { + return tmd_chunks[TMDContentIndex::DLP].id; +} + +void TitleMetadata::SetTitleID(u64 title_id) { + tmd_body.title_id = title_id; +} + +void TitleMetadata::SetTitleType(u32 type) { + tmd_body.title_type = type; +} + +void TitleMetadata::SetTitleVersion(u16 version) { + tmd_body.title_version = version; +} + +void TitleMetadata::SetSystemVersion(u64 version) { + tmd_body.system_version = version; +} + +void TitleMetadata::AddContentChunk(const ContentChunk& chunk) { + tmd_chunks.push_back(chunk); +} + +void TitleMetadata::Print() const { + LOG_DEBUG(Service_FS, "%s - %u chunks", filepath.c_str(), + static_cast(tmd_body.content_count)); + + // Content info describes ranges of content chunks + LOG_DEBUG(Service_FS, "Content info:"); + for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { + if (tmd_body.contentinfo[i].command_count == 0) + break; + + LOG_DEBUG(Service_FS, " Index %04X, Command Count %04X", + static_cast(tmd_body.contentinfo[i].index), + static_cast(tmd_body.contentinfo[i].command_count)); + } + + // For each content info, print their content chunk range + for (size_t i = 0; i < tmd_body.contentinfo.size(); i++) { + u16 index = static_cast(tmd_body.contentinfo[i].index); + u16 count = static_cast(tmd_body.contentinfo[i].command_count); + + if (count == 0) + continue; + + LOG_DEBUG(Service_FS, "Content chunks for content info index %zu:", i); + for (u16 j = index; j < index + count; j++) { + // Don't attempt to print content we don't have + if (j > tmd_body.content_count) + break; + + const ContentChunk& chunk = tmd_chunks[j]; + LOG_DEBUG(Service_FS, " ID %08X, Index %04X, Type %04x, Size %016" PRIX64, + static_cast(chunk.id), static_cast(chunk.index), + static_cast(chunk.type), static_cast(chunk.size)); + } + } +} +} // namespace FileSys diff --git a/src/core/file_sys/title_metadata.h b/src/core/file_sys/title_metadata.h new file mode 100644 index 000000000..1fc157bf3 --- /dev/null +++ b/src/core/file_sys/title_metadata.h @@ -0,0 +1,125 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include "common/common_types.h" +#include "common/swap.h" + +namespace Loader { +enum class ResultStatus; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// FileSys namespace + +namespace FileSys { + +enum TMDSignatureType : u32 { + Rsa4096Sha1 = 0x10000, + Rsa2048Sha1 = 0x10001, + EllipticSha1 = 0x10002, + Rsa4096Sha256 = 0x10003, + Rsa2048Sha256 = 0x10004, + EcdsaSha256 = 0x10005 +}; + +enum TMDContentTypeFlag : u16 { + Encrypted = 1 << 1, + Disc = 1 << 2, + CFM = 1 << 3, + Optional = 1 << 14, + Shared = 1 << 15 +}; + +/** + * Helper which implements an interface to read and write Title Metadata (TMD) files. + * If a file path is provided and the file exists, it can be parsed and used, otherwise + * it must be created. The TMD file can then be interpreted, modified and/or saved. + */ +class TitleMetadata { +public: + struct ContentChunk { + u32_be id; + u16_be index; + u16_be type; + u64_be size; + std::array hash; + }; + + static_assert(sizeof(ContentChunk) == 0x30, "TMD ContentChunk structure size is wrong"); + + struct ContentInfo { + u16_be index; + u16_be command_count; + std::array hash; + }; + + static_assert(sizeof(ContentInfo) == 0x24, "TMD ContentInfo structure size is wrong"); + +#pragma pack(push, 1) + + struct Body { + std::array issuer; + u8 version; + u8 ca_crl_version; + u8 signer_crl_version; + u8 reserved; + u64_be system_version; + u64_be title_id; + u32_be title_type; + u16_be group_id; + u32_be savedata_size; + u32_be srl_private_savedata_size; + std::array reserved_2; + u8 srl_flag; + std::array reserved_3; + u32_be access_rights; + u16_be title_version; + u16_be content_count; + u16_be boot_content; + std::array reserved_4; + std::array contentinfo_hash; + std::array contentinfo; + }; + + static_assert(sizeof(Body) == 0x9C4, "TMD body structure size is wrong"); + +#pragma pack(pop) + + explicit TitleMetadata(std::string& path) : filepath(std::move(path)) {} + Loader::ResultStatus Load(); + Loader::ResultStatus Save(); + + u64 GetTitleID() const; + u32 GetTitleType() const; + u16 GetTitleVersion() const; + u64 GetSystemVersion() const; + size_t GetContentCount() const; + u32 GetBootContentID() const; + u32 GetManualContentID() const; + u32 GetDLPContentID() const; + + void SetTitleID(u64 title_id); + void SetTitleType(u32 type); + void SetTitleVersion(u16 version); + void SetSystemVersion(u64 version); + void AddContentChunk(const ContentChunk& chunk); + + void Print() const; + +private: + enum TMDContentIndex { Main = 0, Manual = 1, DLP = 2 }; + + Body tmd_body; + u32_be signature_type; + std::vector tmd_signature; + std::vector tmd_chunks; + + std::string filepath; +}; + +} // namespace FileSys -- cgit v1.2.3 From 4887d1859102234c594c3140c31217ff64791f37 Mon Sep 17 00:00:00 2001 From: shinyquagsire23 Date: Sun, 1 Oct 2017 10:41:40 -0600 Subject: file_sys, loader: add support for reading TMDs to determine app paths --- src/core/file_sys/archive_ncch.cpp | 15 +++++++++++++-- src/core/loader/ncch.cpp | 17 ++++++++++++++--- 2 files changed, 27 insertions(+), 5 deletions(-) (limited to 'src/core') diff --git a/src/core/file_sys/archive_ncch.cpp b/src/core/file_sys/archive_ncch.cpp index 19e1eb981..e8c5be983 100644 --- a/src/core/file_sys/archive_ncch.cpp +++ b/src/core/file_sys/archive_ncch.cpp @@ -14,6 +14,7 @@ #include "core/file_sys/errors.h" #include "core/file_sys/ivfc_archive.h" #include "core/file_sys/ncch_container.h" +#include "core/file_sys/title_metadata.h" #include "core/hle/service/fs/archive.h" #include "core/loader/loader.h" @@ -27,8 +28,18 @@ static std::string GetNCCHContainerPath(const std::string& nand_directory) { } static std::string GetNCCHPath(const std::string& mount_point, u32 high, u32 low) { - return Common::StringFromFormat("%s%08x/%08x/content/00000000.app", mount_point.c_str(), high, - low); + u32 content_id = 0; + + // TODO(shinyquagsire23): Title database should be doing this path lookup + std::string content_path = + Common::StringFromFormat("%s%08x/%08x/content/", mount_point.c_str(), high, low); + std::string tmd_path = content_path + "00000000.tmd"; + TitleMetadata tmd(tmd_path); + if (tmd.Load() == Loader::ResultStatus::Success) { + content_id = tmd.GetBootContentID(); + } + + return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id); } ArchiveFactory_NCCH::ArchiveFactory_NCCH(const std::string& nand_directory) diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 66bc5823d..52686e364 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -14,6 +14,7 @@ #include "core/core.h" #include "core/file_sys/archive_selfncch.h" #include "core/file_sys/ncch_container.h" +#include "core/file_sys/title_metadata.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/hle/service/cfg/cfg.h" @@ -49,9 +50,19 @@ static std::string GetUpdateNCCHPath(u64_le program_id) { u32 high = static_cast((program_id | UPDATE_MASK) >> 32); u32 low = static_cast((program_id | UPDATE_MASK) & 0xFFFFFFFF); - return Common::StringFromFormat("%sNintendo 3DS/%s/%s/title/%08x/%08x/content/00000000.app", - FileUtil::GetUserPath(D_SDMC_IDX).c_str(), SYSTEM_ID, SDCARD_ID, - high, low); + // TODO(shinyquagsire23): Title database should be doing this path lookup + std::string content_path = Common::StringFromFormat( + "%sNintendo 3DS/%s/%s/title/%08x/%08x/content/", FileUtil::GetUserPath(D_SDMC_IDX).c_str(), + SYSTEM_ID, SDCARD_ID, high, low); + std::string tmd_path = content_path + "00000000.tmd"; + + u32 content_id = 0; + FileSys::TitleMetadata tmd(tmd_path); + if (tmd.Load() == ResultStatus::Success) { + content_id = tmd.GetBootContentID(); + } + + return Common::StringFromFormat("%s%08x.app", content_path.c_str(), content_id); } std::pair, ResultStatus> AppLoader_NCCH::LoadKernelSystemMode() { -- cgit v1.2.3 From 8217ed7acb71bfa574e0a29e69b902a0c539c814 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 29 Sep 2017 14:47:52 -0500 Subject: Kernel/Thread: Added a helper function to get a thread's command buffer VAddr. --- src/core/hle/kernel/thread.cpp | 6 ++++++ src/core/hle/kernel/thread.h | 6 ++++++ 2 files changed, 12 insertions(+) (limited to 'src/core') diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 11f7d2127..6ebc8c151 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -534,6 +534,12 @@ s32 Thread::GetWaitObjectIndex(WaitObject* object) const { return static_cast(std::distance(match, wait_objects.rend()) - 1); } +VAddr Thread::GetCommandBufferAddress() const { + // Offset from the start of TLS at which the IPC command buffer begins. + static constexpr int CommandHeaderOffset = 0x80; + return GetTLSAddress() + CommandHeaderOffset; +} + //////////////////////////////////////////////////////////////////////////////////////////////////// void ThreadingInit() { diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h index f02e1d43a..520ac22c2 100644 --- a/src/core/hle/kernel/thread.h +++ b/src/core/hle/kernel/thread.h @@ -159,6 +159,12 @@ public: return tls_address; } + /* + * Returns the address of the current thread's command buffer, located in the TLS. + * @returns VAddr of the thread's command buffer. + */ + VAddr GetCommandBufferAddress() const; + /** * Returns whether this thread is waiting for all the objects in * its wait list to become ready, as a result of a WaitSynchronizationN call -- cgit v1.2.3 From 811c01e5fec3a1cc7a6faf5eaed11d1aaef31768 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 29 Sep 2017 19:38:54 -0500 Subject: Memory: Make ReadBlock take a Process parameter on which to operate --- src/core/memory.cpp | 40 ++++++++++++++++++++++++++++------------ src/core/memory.h | 2 ++ 2 files changed, 30 insertions(+), 12 deletions(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 847e69710..da97038c6 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -82,10 +82,10 @@ void UnmapRegion(PageTable& page_table, VAddr base, u32 size) { * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) * using a VMA from the current process */ -static u8* GetPointerFromVMA(VAddr vaddr) { +static u8* GetPointerFromVMA(const Kernel::Process& process, VAddr vaddr) { u8* direct_pointer = nullptr; - auto& vm_manager = Kernel::g_current_process->vm_manager; + auto& vm_manager = process.vm_manager; auto it = vm_manager.FindVMA(vaddr); ASSERT(it != vm_manager.vma_map.end()); @@ -107,6 +107,14 @@ static u8* GetPointerFromVMA(VAddr vaddr) { return direct_pointer + (vaddr - vma.base); } +/** + * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) + * using a VMA from the current process. + */ +static u8* GetPointerFromVMA(VAddr vaddr) { + return GetPointerFromVMA(*Kernel::g_current_process, vaddr); +} + /** * This function should only be called for virtual addreses with attribute `PageType::Special`. */ @@ -470,7 +478,10 @@ u64 Read64(const VAddr addr) { return Read(addr); } -void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { +void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, + const size_t size) { + auto& page_table = process.vm_manager.page_table; + size_t remaining_size = size; size_t page_index = src_addr >> PAGE_BITS; size_t page_offset = src_addr & PAGE_MASK; @@ -479,7 +490,7 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - switch (current_page_table->attributes[page_index]) { + switch (page_table.attributes[page_index]) { case PageType::Unmapped: { LOG_ERROR(HW_Memory, "unmapped ReadBlock @ 0x%08X (start address = 0x%08X, size = %zu)", current_vaddr, src_addr, size); @@ -487,29 +498,30 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { break; } case PageType::Memory: { - DEBUG_ASSERT(current_page_table->pointers[page_index]); + DEBUG_ASSERT(page_table.pointers[page_index]); - const u8* src_ptr = current_page_table->pointers[page_index] + page_offset; + const u8* src_ptr = page_table.pointers[page_index] + page_offset; std::memcpy(dest_buffer, src_ptr, copy_amount); break; } case PageType::Special: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - - GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); + handler->ReadBlock(current_vaddr, dest_buffer, copy_amount); break; } case PageType::RasterizerCachedMemory: { RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::Flush); - std::memcpy(dest_buffer, GetPointerFromVMA(current_vaddr), copy_amount); + std::memcpy(dest_buffer, GetPointerFromVMA(process, current_vaddr), copy_amount); break; } case PageType::RasterizerCachedSpecial: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::Flush); - GetMMIOHandler(current_vaddr)->ReadBlock(current_vaddr, dest_buffer, copy_amount); + handler->ReadBlock(current_vaddr, dest_buffer, copy_amount); break; } default: @@ -523,6 +535,10 @@ void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { } } +void ReadBlock(const VAddr src_addr, void* dest_buffer, const size_t size) { + ReadBlock(*Kernel::g_current_process, src_addr, dest_buffer, size); +} + void Write8(const VAddr addr, const u8 data) { Write(addr, data); } diff --git a/src/core/memory.h b/src/core/memory.h index 347c08c78..5d4eb56a9 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -205,6 +205,8 @@ void Write16(VAddr addr, u16 data); void Write32(VAddr addr, u32 data); void Write64(VAddr addr, u64 data); +void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, + size_t size); void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); void ZeroBlock(const VAddr dest_addr, const size_t size); -- cgit v1.2.3 From 1f2de7501b427f9f5ac1e5d244f8ec52eca9bc64 Mon Sep 17 00:00:00 2001 From: Subv Date: Fri, 29 Sep 2017 22:42:25 -0500 Subject: Memory: Make WriteBlock take a Process parameter on which to operate --- src/core/memory.cpp | 27 +++++++++++++++++---------- src/core/memory.h | 2 ++ 2 files changed, 19 insertions(+), 10 deletions(-) (limited to 'src/core') diff --git a/src/core/memory.cpp b/src/core/memory.cpp index da97038c6..7f58be6de 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -555,7 +555,9 @@ void Write64(const VAddr addr, const u64 data) { Write(addr, data); } -void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { +void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, + const size_t size) { + auto& page_table = process.vm_manager.page_table; size_t remaining_size = size; size_t page_index = dest_addr >> PAGE_BITS; size_t page_offset = dest_addr & PAGE_MASK; @@ -564,7 +566,7 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size const size_t copy_amount = std::min(PAGE_SIZE - page_offset, remaining_size); const VAddr current_vaddr = static_cast((page_index << PAGE_BITS) + page_offset); - switch (current_page_table->attributes[page_index]) { + switch (page_table.attributes[page_index]) { case PageType::Unmapped: { LOG_ERROR(HW_Memory, "unmapped WriteBlock @ 0x%08X (start address = 0x%08X, size = %zu)", @@ -572,29 +574,30 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size break; } case PageType::Memory: { - DEBUG_ASSERT(current_page_table->pointers[page_index]); + DEBUG_ASSERT(page_table.pointers[page_index]); - u8* dest_ptr = current_page_table->pointers[page_index] + page_offset; + u8* dest_ptr = page_table.pointers[page_index] + page_offset; std::memcpy(dest_ptr, src_buffer, copy_amount); break; } case PageType::Special: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); - - GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); + handler->WriteBlock(current_vaddr, src_buffer, copy_amount); break; } case PageType::RasterizerCachedMemory: { RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::FlushAndInvalidate); - std::memcpy(GetPointerFromVMA(current_vaddr), src_buffer, copy_amount); + std::memcpy(GetPointerFromVMA(process, current_vaddr), src_buffer, copy_amount); break; } case PageType::RasterizerCachedSpecial: { - DEBUG_ASSERT(GetMMIOHandler(current_vaddr)); + MMIORegionPointer handler = GetMMIOHandler(page_table, current_vaddr); + DEBUG_ASSERT(handler); RasterizerFlushVirtualRegion(current_vaddr, static_cast(copy_amount), FlushMode::FlushAndInvalidate); - GetMMIOHandler(current_vaddr)->WriteBlock(current_vaddr, src_buffer, copy_amount); + handler->WriteBlock(current_vaddr, src_buffer, copy_amount); break; } default: @@ -608,6 +611,10 @@ void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size } } +void WriteBlock(const VAddr dest_addr, const void* src_buffer, const size_t size) { + WriteBlock(*Kernel::g_current_process, dest_addr, src_buffer, size); +} + void ZeroBlock(const VAddr dest_addr, const size_t size) { size_t remaining_size = size; size_t page_index = dest_addr >> PAGE_BITS; diff --git a/src/core/memory.h b/src/core/memory.h index 5d4eb56a9..dd599f73e 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -208,6 +208,8 @@ void Write64(VAddr addr, u64 data); void ReadBlock(const Kernel::Process& process, const VAddr src_addr, void* dest_buffer, size_t size); void ReadBlock(const VAddr src_addr, void* dest_buffer, size_t size); +void WriteBlock(const Kernel::Process& process, const VAddr dest_addr, const void* src_buffer, + size_t size); void WriteBlock(const VAddr dest_addr, const void* src_buffer, size_t size); void ZeroBlock(const VAddr dest_addr, const size_t size); void CopyBlock(VAddr dest_addr, VAddr src_addr, size_t size); -- cgit v1.2.3 From b18589ecf780ca479e077529b789ec481e58f715 Mon Sep 17 00:00:00 2001 From: Subv Date: Sun, 1 Oct 2017 13:57:50 -0500 Subject: Kernel/SharedMemory: Don't take over and unmap the source memory block when creating a shared memory, just reference it. Also reference the right offset into the backing block for the requested address. --- src/core/hle/kernel/shared_memory.cpp | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index 02d5a7a36..d45daca35 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -55,22 +55,19 @@ SharedPtr SharedMemory::Create(SharedPtr owner_process, u Kernel::g_current_process->vm_manager.RefreshMemoryBlockMappings(linheap_memory.get()); } } else { - // TODO(Subv): What happens if an application tries to create multiple memory blocks - // pointing to the same address? auto& vm_manager = shared_memory->owner_process->vm_manager; // The memory is already available and mapped in the owner process. - auto vma = vm_manager.FindVMA(address)->second; - // Copy it over to our own storage - shared_memory->backing_block = std::make_shared>( - vma.backing_block->data() + vma.offset, vma.backing_block->data() + vma.offset + size); - shared_memory->backing_block_offset = 0; - // Unmap the existing pages - vm_manager.UnmapRange(address, size); - // Map our own block into the address space - vm_manager.MapMemoryBlock(address, shared_memory->backing_block, 0, size, - MemoryState::Shared); - // Reprotect the block with the new permissions - vm_manager.ReprotectRange(address, size, ConvertPermissions(permissions)); + auto vma = vm_manager.FindVMA(address); + ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address"); + ASSERT_MSG(vma->second.backing_block, "Backing block doesn't exist for address"); + + // The returned VMA might be a bigger one encompassing the desired address. + auto vma_offset = address - vma->first; + ASSERT_MSG(vma_offset + size <= vma->second.size, + "Shared memory exceeds bounds of mapped block"); + + shared_memory->backing_block = vma->second.backing_block; + shared_memory->backing_block_offset = vma->second.offset + vma_offset; } shared_memory->base_address = address; @@ -184,4 +181,4 @@ u8* SharedMemory::GetPointer(u32 offset) { return backing_block->data() + backing_block_offset + offset; } -} // namespace +} // namespace Kernel -- cgit v1.2.3 From 7772fc07314d568939b0e9c12e4ead47e35d3c86 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 11:33:32 -0500 Subject: Memory: Remove all GetPointer usages from the GDB stub. --- src/core/gdbstub/gdbstub.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src/core') diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index be2b2e25f..d6be16ef6 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -644,7 +644,7 @@ static void ReadMemory() { auto start_offset = command_buffer + 1; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); + VAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); start_offset = addr_pos + 1; u32 len = @@ -656,12 +656,14 @@ static void ReadMemory() { SendReply("E01"); } - const u8* data = Memory::GetPointer(addr); - if (!data) { + if (!Memory::IsValidVirtualAddress(addr)) { return SendReply("E00"); } - MemToGdbHex(reply, data, len); + std::vector data(len); + Memory::ReadBlock(addr, data.data(), len); + + MemToGdbHex(reply, data.data(), len); reply[len * 2] = '\0'; SendReply(reinterpret_cast(reply)); } @@ -670,18 +672,20 @@ static void ReadMemory() { static void WriteMemory() { auto start_offset = command_buffer + 1; auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - PAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); + VAddr addr = HexToInt(start_offset, static_cast(addr_pos - start_offset)); start_offset = addr_pos + 1; auto len_pos = std::find(start_offset, command_buffer + command_length, ':'); u32 len = HexToInt(start_offset, static_cast(len_pos - start_offset)); - u8* dst = Memory::GetPointer(addr); - if (!dst) { + if (!Memory::IsValidVirtualAddress(addr)) { return SendReply("E00"); } - GdbHexToMem(dst, len_pos + 1, len); + std::vector data(len); + + GdbHexToMem(data.data(), len_pos + 1, len); + Memory::WriteBlock(addr, data.data(), len); SendReply("OK"); } -- cgit v1.2.3 From b863d6c86043226c8c88749c4e0eecaf9865c569 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 11:40:40 -0500 Subject: SVC: Replace GetPointer usage with Read32 in WaitSynchronizationN. --- src/core/hle/function_wrappers.h | 8 ++++---- src/core/hle/svc.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 5e6002f4e..17892d81c 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -58,12 +58,12 @@ void Wrap() { FuncReturn(retval); } -template +template void Wrap() { s32 param_1 = 0; - s32 retval = func(¶m_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), - (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) - .raw; + s32 retval = + func(¶m_1, PARAM(1), (s32)PARAM(2), (PARAM(3) != 0), (((s64)PARAM(4) << 32) | PARAM(0))) + .raw; Core::CPU().SetReg(1, (u32)param_1); FuncReturn(retval); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 6be5db13f..9edce7ab7 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -303,12 +303,11 @@ static ResultCode WaitSynchronization1(Kernel::Handle handle, s64 nano_seconds) } /// Wait for the given handles to synchronize, timeout after the specified nanoseconds -static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 handle_count, +static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 handle_count, bool wait_all, s64 nano_seconds) { Kernel::Thread* thread = Kernel::GetCurrentThread(); - // Check if 'handles' is invalid - if (handles == nullptr) + if (!Memory::IsValidVirtualAddress(handles_address)) return Kernel::ERR_INVALID_POINTER; // NOTE: on real hardware, there is no nullptr check for 'out' (tested with firmware 4.4). If @@ -323,7 +322,8 @@ static ResultCode WaitSynchronizationN(s32* out, Kernel::Handle* handles, s32 ha std::vector objects(handle_count); for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.Get(handles[i]); + Kernel::Handle handle = Memory::Read32(handles_address + i * sizeof(Kernel::Handle)); + auto object = Kernel::g_handle_table.Get(handle); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; -- cgit v1.2.3 From 0cfb231e002629172337c048e8cabc8c653e34e3 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 11:49:29 -0500 Subject: SVC: Replace GetPointer usage with Read32 in ReplyAndReceive. --- src/core/hle/function_wrappers.h | 5 ++--- src/core/hle/svc.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 7 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index 17892d81c..cd500e83d 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -69,11 +69,10 @@ void Wrap() { FuncReturn(retval); } -template +template void Wrap() { s32 param_1 = 0; - u32 retval = - func(¶m_1, (Kernel::Handle*)Memory::GetPointer(PARAM(1)), (s32)PARAM(2), PARAM(3)).raw; + u32 retval = func(¶m_1, PARAM(1), (s32)PARAM(2), PARAM(3)).raw; Core::CPU().SetReg(1, (u32)param_1); FuncReturn(retval); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 9edce7ab7..61360bede 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -452,10 +452,9 @@ static ResultCode WaitSynchronizationN(s32* out, VAddr handles_address, s32 hand } /// In a single operation, sends a IPC reply and waits for a new request. -static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handle_count, +static ResultCode ReplyAndReceive(s32* index, VAddr handles_address, s32 handle_count, Kernel::Handle reply_target) { - // 'handles' has to be a valid pointer even if 'handle_count' is 0. - if (handles == nullptr) + if (!Memory::IsValidVirtualAddress(handles_address)) return Kernel::ERR_INVALID_POINTER; // Check if 'handle_count' is invalid @@ -466,7 +465,8 @@ static ResultCode ReplyAndReceive(s32* index, Kernel::Handle* handles, s32 handl std::vector objects(handle_count); for (int i = 0; i < handle_count; ++i) { - auto object = Kernel::g_handle_table.Get(handles[i]); + Kernel::Handle handle = Memory::Read32(handles_address + i * sizeof(Kernel::Handle)); + auto object = Kernel::g_handle_table.Get(handle); if (object == nullptr) return ERR_INVALID_HANDLE; objects[i] = object; -- cgit v1.2.3 From 3c0113632dc4fb4e55b5dad9278a5b766dcaec14 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 11:52:39 -0500 Subject: SVC: Replace GetPointer usage with ReadBlock in OutputDebugString. --- src/core/hle/function_wrappers.h | 4 ++-- src/core/hle/svc.cpp | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index cd500e83d..cb0b430ee 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -267,9 +267,9 @@ void Wrap() { func(((s64)PARAM(1) << 32) | PARAM(0)); } -template +template void Wrap() { - func((char*)Memory::GetPointer(PARAM(0)), PARAM(1)); + func(PARAM(0), PARAM(1)); } template diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 61360bede..37eeeb860 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -619,8 +619,10 @@ 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, int len) { - LOG_DEBUG(Debug_Emulated, "%.*s", len, string); +static void OutputDebugString(VAddr address, int len) { + std::vector string(len); + Memory::ReadBlock(address, string.data(), len); + LOG_DEBUG(Debug_Emulated, "%.*s", len, string.data()); } /// Get resource limit -- cgit v1.2.3 From 7b09b30ef11d1d4001a50bbb91abdfb86b954ce2 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 12:05:13 -0500 Subject: SVC: Replace GetPointer usage with ReadCString in ConnectToPort. --- src/core/hle/function_wrappers.h | 15 --------------- src/core/hle/svc.cpp | 14 +++++++++----- 2 files changed, 9 insertions(+), 20 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index cb0b430ee..a982b2b54 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -151,21 +151,6 @@ void Wrap() { FuncReturn(func(PARAM(0)).raw); } -template -void Wrap() { - FuncReturn(func((s64*)Memory::GetPointer(PARAM(0)), PARAM(1), - (u32*)Memory::GetPointer(PARAM(2)), (s32)PARAM(3)) - .raw); -} - -template -void Wrap() { - u32 param_1 = 0; - u32 retval = func(¶m_1, (char*)Memory::GetPointer(PARAM(1))).raw; - Core::CPU().SetReg(1, param_1); - FuncReturn(retval); -} - template void Wrap() { u32 param_1 = 0; diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 37eeeb860..2ae177ab5 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -201,17 +201,21 @@ static ResultCode UnmapMemoryBlock(Kernel::Handle handle, u32 addr) { } /// Connect to an OS service given the port name, returns the handle to the port to out -static ResultCode ConnectToPort(Kernel::Handle* out_handle, const char* port_name) { - if (port_name == nullptr) +static ResultCode ConnectToPort(Kernel::Handle* out_handle, VAddr port_name_address) { + if (!Memory::IsValidVirtualAddress(port_name_address)) return Kernel::ERR_NOT_FOUND; - if (std::strlen(port_name) > 11) + + static constexpr std::size_t PortNameMaxLength = 11; + // Read 1 char beyond the max allowed port name to detect names that are too long. + std::string port_name = Memory::ReadCString(port_name_address, PortNameMaxLength + 1); + if (port_name.size() > PortNameMaxLength) return Kernel::ERR_PORT_NAME_TOO_LONG; - LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name); + LOG_TRACE(Kernel_SVC, "called port_name=%s", port_name.c_str()); auto it = Service::g_kernel_named_ports.find(port_name); if (it == Service::g_kernel_named_ports.end()) { - LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name); + LOG_WARNING(Kernel_SVC, "tried to connect to unknown port: %s", port_name.c_str()); return Kernel::ERR_NOT_FOUND; } -- cgit v1.2.3 From 46fc7595b4f5f161ecd5ae21259c661c15ca46f3 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 12:11:55 -0500 Subject: SVC: Remove GetPointer usage in CreatePort. --- src/core/hle/function_wrappers.h | 6 ++---- src/core/hle/svc.cpp | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/function_wrappers.h b/src/core/hle/function_wrappers.h index a982b2b54..f93439f21 100644 --- a/src/core/hle/function_wrappers.h +++ b/src/core/hle/function_wrappers.h @@ -206,13 +206,11 @@ void Wrap() { FuncReturn(func(PARAM(0), PARAM(1)).raw); } -template +template void Wrap() { Kernel::Handle param_1 = 0; Kernel::Handle param_2 = 0; - u32 retval = func(¶m_1, ¶m_2, - reinterpret_cast(Memory::GetPointer(PARAM(2))), PARAM(3)) - .raw; + u32 retval = func(¶m_1, ¶m_2, PARAM(2), PARAM(3)).raw; Core::CPU().SetReg(1, param_1); Core::CPU().SetReg(2, param_2); FuncReturn(retval); diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2ae177ab5..b72ca3581 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -1104,9 +1104,9 @@ static ResultCode CreateMemoryBlock(Kernel::Handle* out_handle, u32 addr, u32 si } static ResultCode CreatePort(Kernel::Handle* server_port, Kernel::Handle* client_port, - const char* name, u32 max_sessions) { + VAddr name_address, u32 max_sessions) { // TODO(Subv): Implement named ports. - ASSERT_MSG(name == nullptr, "Named ports are currently unimplemented"); + ASSERT_MSG(name_address == 0, "Named ports are currently unimplemented"); using Kernel::ServerPort; using Kernel::ClientPort; -- cgit v1.2.3 From 97f262c1f5c39e51d6fe2e32429610599299db60 Mon Sep 17 00:00:00 2001 From: Subv Date: Wed, 4 Oct 2017 12:23:08 -0500 Subject: SVC: Removed GetPointer usage in the GetResourceLimit functions. --- src/core/hle/svc.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) (limited to 'src/core') diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index b72ca3581..e8ca419d5 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -644,9 +644,9 @@ static ResultCode GetResourceLimit(Kernel::Handle* resource_limit, Kernel::Handl } /// Get resource limit current values -static ResultCode GetResourceLimitCurrentValues(s64* values, Kernel::Handle resource_limit_handle, - u32* names, u32 name_count) { - LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", +static ResultCode GetResourceLimitCurrentValues(VAddr values, Kernel::Handle resource_limit_handle, + VAddr names, u32 name_count) { + LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%08X, name_count=%d", resource_limit_handle, names, name_count); SharedPtr resource_limit = @@ -654,16 +654,19 @@ static ResultCode GetResourceLimitCurrentValues(s64* values, Kernel::Handle reso if (resource_limit == nullptr) return ERR_INVALID_HANDLE; - for (unsigned int i = 0; i < name_count; ++i) - values[i] = resource_limit->GetCurrentResourceValue(names[i]); + for (unsigned int i = 0; i < name_count; ++i) { + u32 name = Memory::Read32(names + i * sizeof(u32)); + s64 value = resource_limit->GetCurrentResourceValue(name); + Memory::Write64(values + i * sizeof(u64), value); + } return RESULT_SUCCESS; } /// Get resource limit max values -static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resource_limit_handle, - u32* names, u32 name_count) { - LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%p, name_count=%d", +static ResultCode GetResourceLimitLimitValues(VAddr values, Kernel::Handle resource_limit_handle, + VAddr names, u32 name_count) { + LOG_TRACE(Kernel_SVC, "called resource_limit=%08X, names=%08X, name_count=%d", resource_limit_handle, names, name_count); SharedPtr resource_limit = @@ -671,8 +674,11 @@ static ResultCode GetResourceLimitLimitValues(s64* values, Kernel::Handle resour if (resource_limit == nullptr) return ERR_INVALID_HANDLE; - for (unsigned int i = 0; i < name_count; ++i) - values[i] = resource_limit->GetMaxResourceValue(names[i]); + for (unsigned int i = 0; i < name_count; ++i) { + u32 name = Memory::Read32(names + i * sizeof(u32)); + s64 value = resource_limit->GetMaxResourceValue(names); + Memory::Write64(values + i * sizeof(u64), value); + } return RESULT_SUCCESS; } -- cgit v1.2.3 From 83e5f639e626bcbd41fe86566a6d15d2d473536d Mon Sep 17 00:00:00 2001 From: Dragios Date: Mon, 9 Oct 2017 09:10:48 +0800 Subject: Change command header in nwm::UDS Initialize function --- src/core/hle/service/nwm/nwm_uds.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index 0aa63cc1e..87a6b0eca 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -975,7 +975,7 @@ void OnClientConnected(u16 network_node_id) { } const Interface::FunctionInfo FunctionTable[] = { - {0x00010442, nullptr, "Initialize (deprecated)"}, + {0x000102C2, nullptr, "Initialize (deprecated)"}, {0x00020000, nullptr, "Scrap"}, {0x00030000, Shutdown, "Shutdown"}, {0x00040402, nullptr, "CreateNetwork (deprecated)"}, -- cgit v1.2.3