summaryrefslogtreecommitdiffstats
path: root/src/core/hle
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/hle')
-rw-r--r--src/core/hle/ipc_helpers.h5
-rw-r--r--src/core/hle/kernel/client_session.cpp1
-rw-r--r--src/core/hle/kernel/client_session.h4
-rw-r--r--src/core/hle/kernel/errors.h76
-rw-r--r--src/core/hle/kernel/handle_table.cpp16
-rw-r--r--src/core/hle/kernel/handle_table.h18
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp34
-rw-r--r--src/core/hle/kernel/hle_ipc.h29
-rw-r--r--src/core/hle/kernel/kernel.cpp96
-rw-r--r--src/core/hle/kernel/kernel.h10
-rw-r--r--src/core/hle/kernel/mutex.cpp2
-rw-r--r--src/core/hle/kernel/object.cpp6
-rw-r--r--src/core/hle/kernel/object.h9
-rw-r--r--src/core/hle/kernel/process.cpp239
-rw-r--r--src/core/hle/kernel/process.h159
-rw-r--r--src/core/hle/kernel/process_capability.cpp355
-rw-r--r--src/core/hle/kernel/process_capability.h264
-rw-r--r--src/core/hle/kernel/readable_event.cpp (renamed from src/core/hle/kernel/event.cpp)37
-rw-r--r--src/core/hle/kernel/readable_event.h (renamed from src/core/hle/kernel/event.h)48
-rw-r--r--src/core/hle/kernel/resource_limit.cpp75
-rw-r--r--src/core/hle/kernel/resource_limit.h102
-rw-r--r--src/core/hle/kernel/scheduler.cpp66
-rw-r--r--src/core/hle/kernel/scheduler.h69
-rw-r--r--src/core/hle/kernel/server_session.cpp5
-rw-r--r--src/core/hle/kernel/server_session.h1
-rw-r--r--src/core/hle/kernel/shared_memory.cpp36
-rw-r--r--src/core/hle/kernel/shared_memory.h52
-rw-r--r--src/core/hle/kernel/svc.cpp1063
-rw-r--r--src/core/hle/kernel/svc.h16
-rw-r--r--src/core/hle/kernel/svc_wrap.h60
-rw-r--r--src/core/hle/kernel/thread.cpp103
-rw-r--r--src/core/hle/kernel/thread.h44
-rw-r--r--src/core/hle/kernel/vm_manager.cpp213
-rw-r--r--src/core/hle/kernel/vm_manager.h329
-rw-r--r--src/core/hle/kernel/wait_object.cpp2
-rw-r--r--src/core/hle/kernel/wait_object.h1
-rw-r--r--src/core/hle/kernel/writable_event.cpp52
-rw-r--r--src/core/hle/kernel/writable_event.h61
-rw-r--r--src/core/hle/result.h2
-rw-r--r--src/core/hle/service/acc/acc.cpp44
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_su.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/acc/acc_u1.cpp2
-rw-r--r--src/core/hle/service/acc/profile_manager.cpp30
-rw-r--r--src/core/hle/service/acc/profile_manager.h30
-rw-r--r--src/core/hle/service/am/am.cpp657
-rw-r--r--src/core/hle/service/am/am.h69
-rw-r--r--src/core/hle/service/am/applet_ae.cpp100
-rw-r--r--src/core/hle/service/am/applet_ae.h6
-rw-r--r--src/core/hle/service/am/applet_oe.cpp48
-rw-r--r--src/core/hle/service/am/applet_oe.h6
-rw-r--r--src/core/hle/service/am/applets/applets.cpp114
-rw-r--r--src/core/hle/service/am/applets/applets.h109
-rw-r--r--src/core/hle/service/am/applets/profile_select.cpp77
-rw-r--r--src/core/hle/service/am/applets/profile_select.h50
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.cpp160
-rw-r--r--src/core/hle/service/am/applets/software_keyboard.h74
-rw-r--r--src/core/hle/service/am/applets/stub_applet.cpp70
-rw-r--r--src/core/hle/service/am/applets/stub_applet.h24
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp32
-rw-r--r--src/core/hle/service/aoc/aoc_u.h6
-rw-r--r--src/core/hle/service/apm/interface.cpp16
-rw-r--r--src/core/hle/service/arp/arp.cpp4
-rw-r--r--src/core/hle/service/audio/audout_u.cpp48
-rw-r--r--src/core/hle/service/audio/audout_u.h3
-rw-r--r--src/core/hle/service/audio/audren_u.cpp102
-rw-r--r--src/core/hle/service/audio/hwopus.cpp50
-rw-r--r--src/core/hle/service/bcat/module.cpp3
-rw-r--r--src/core/hle/service/btdrv/btdrv.cpp43
-rw-r--r--src/core/hle/service/btm/btm.cpp121
-rw-r--r--src/core/hle/service/erpt/erpt.cpp12
-rw-r--r--src/core/hle/service/fatal/fatal.cpp3
-rw-r--r--src/core/hle/service/fgm/fgm.cpp4
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp104
-rw-r--r--src/core/hle/service/filesystem/filesystem.h22
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp234
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h1
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.cpp43
-rw-r--r--src/core/hle/service/hid/controllers/debug_pad.h41
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp20
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.h7
-rw-r--r--src/core/hle/service/hid/controllers/mouse.cpp25
-rw-r--r--src/core/hle/service/hid/controllers/mouse.h9
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp488
-rw-r--r--src/core/hle/service/hid/controllers/npad.h56
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp13
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h12
-rw-r--r--src/core/hle/service/hid/hid.cpp278
-rw-r--r--src/core/hle/service/hid/irs.cpp54
-rw-r--r--src/core/hle/service/lbl/lbl.cpp12
-rw-r--r--src/core/hle/service/ldn/ldn.cpp12
-rw-r--r--src/core/hle/service/ldr/ldr.cpp392
-rw-r--r--src/core/hle/service/lm/lm.cpp17
-rw-r--r--src/core/hle/service/mm/mm_u.cpp14
-rw-r--r--src/core/hle/service/nfc/nfc.cpp24
-rw-r--r--src/core/hle/service/nfp/nfp.cpp58
-rw-r--r--src/core/hle/service/nfp/nfp.h7
-rw-r--r--src/core/hle/service/nifm/nifm.cpp43
-rw-r--r--src/core/hle/service/nim/nim.cpp43
-rw-r--r--src/core/hle/service/ns/ns.cpp72
-rw-r--r--src/core/hle/service/ns/pl_u.cpp18
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp2
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp20
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h6
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp29
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp1
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp16
-rw-r--r--src/core/hle/service/nvdrv/interface.cpp33
-rw-r--r--src/core/hle/service/nvdrv/interface.h9
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp32
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h22
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp25
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h11
-rw-r--r--src/core/hle/service/pctl/module.cpp8
-rw-r--r--src/core/hle/service/pm/pm.cpp4
-rw-r--r--src/core/hle/service/psc/psc.cpp4
-rw-r--r--src/core/hle/service/service.cpp35
-rw-r--r--src/core/hle/service/service.h8
-rw-r--r--src/core/hle/service/set/set.cpp52
-rw-r--r--src/core/hle/service/set/set.h1
-rw-r--r--src/core/hle/service/set/set_sys.cpp8
-rw-r--r--src/core/hle/service/sm/controller.cpp11
-rw-r--r--src/core/hle/service/sm/sm.cpp67
-rw-r--r--src/core/hle/service/sm/sm.h3
-rw-r--r--src/core/hle/service/spl/module.cpp13
-rw-r--r--src/core/hle/service/spl/module.h4
-rw-r--r--src/core/hle/service/ssl/ssl.cpp2
-rw-r--r--src/core/hle/service/time/interface.cpp5
-rw-r--r--src/core/hle/service/time/time.cpp172
-rw-r--r--src/core/hle/service/time/time.h28
-rw-r--r--src/core/hle/service/usb/usb.cpp12
-rw-r--r--src/core/hle/service/vi/vi.cpp256
135 files changed, 6879 insertions, 2023 deletions
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index a4bfe2eb0..0d6c85aed 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -18,7 +18,7 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_port.h"
+#include "core/hle/kernel/server_session.h"
namespace IPC {
@@ -117,8 +117,7 @@ public:
AlignWithPadding();
- const bool request_has_domain_header{context.GetDomainMessageHeader() != nullptr};
- if (context.Session()->IsDomain() && request_has_domain_header) {
+ if (context.Session()->IsDomain() && context.HasDomainMessageHeader()) {
IPC::DomainMessageHeader domain_header{};
domain_header.num_objects = num_domain_objects;
PushRaw(domain_header);
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index c114eaf99..704e82824 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -8,6 +8,7 @@
#include "core/hle/kernel/server_session.h"
#include "core/hle/kernel/session.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/result.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index 439fbdb35..4c18de69c 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -6,9 +6,9 @@
#include <memory>
#include <string>
-#include "common/common_types.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/result.h"
+
+union ResultCode;
namespace Kernel {
diff --git a/src/core/hle/kernel/errors.h b/src/core/hle/kernel/errors.h
index ee698c8a7..d17eb0cb6 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -8,58 +8,30 @@
namespace Kernel {
-namespace ErrCodes {
-enum {
- // Confirmed Switch OS error codes
- MaxConnectionsReached = 7,
- InvalidSize = 101,
- InvalidAddress = 102,
- HandleTableFull = 105,
- InvalidMemoryState = 106,
- InvalidMemoryPermissions = 108,
- InvalidMemoryRange = 110,
- InvalidThreadPriority = 112,
- InvalidProcessorId = 113,
- InvalidHandle = 114,
- InvalidPointer = 115,
- InvalidCombination = 116,
- Timeout = 117,
- SynchronizationCanceled = 118,
- TooLarge = 119,
- InvalidEnumValue = 120,
- NoSuchEntry = 121,
- AlreadyRegistered = 122,
- SessionClosed = 123,
- InvalidState = 125,
- ResourceLimitExceeded = 132,
-};
-}
+// Confirmed Switch kernel error codes
-// WARNING: The kernel is quite inconsistent in it's usage of errors code. Make sure to always
-// double check that the code matches before re-using the constant.
-
-constexpr ResultCode ERR_HANDLE_TABLE_FULL(ErrorModule::Kernel, ErrCodes::HandleTableFull);
-constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE(ErrorModule::Kernel, ErrCodes::SessionClosed);
-constexpr ResultCode ERR_PORT_NAME_TOO_LONG(ErrorModule::Kernel, ErrCodes::TooLarge);
-constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED(ErrorModule::Kernel,
- ErrCodes::MaxConnectionsReached);
-constexpr ResultCode ERR_INVALID_ENUM_VALUE(ErrorModule::Kernel, ErrCodes::InvalidEnumValue);
-constexpr ResultCode ERR_INVALID_COMBINATION_KERNEL(ErrorModule::Kernel,
- ErrCodes::InvalidCombination);
-constexpr ResultCode ERR_INVALID_ADDRESS(ErrorModule::Kernel, ErrCodes::InvalidAddress);
-constexpr ResultCode ERR_INVALID_ADDRESS_STATE(ErrorModule::Kernel, ErrCodes::InvalidMemoryState);
-constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS(ErrorModule::Kernel,
- ErrCodes::InvalidMemoryPermissions);
-constexpr ResultCode ERR_INVALID_MEMORY_RANGE(ErrorModule::Kernel, ErrCodes::InvalidMemoryRange);
-constexpr ResultCode ERR_INVALID_HANDLE(ErrorModule::Kernel, ErrCodes::InvalidHandle);
-constexpr ResultCode ERR_INVALID_PROCESSOR_ID(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
-constexpr ResultCode ERR_INVALID_SIZE(ErrorModule::Kernel, ErrCodes::InvalidSize);
-constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::Kernel, ErrCodes::AlreadyRegistered);
-constexpr ResultCode ERR_INVALID_STATE(ErrorModule::Kernel, ErrCodes::InvalidState);
-constexpr ResultCode ERR_INVALID_THREAD_PRIORITY(ErrorModule::Kernel,
- ErrCodes::InvalidThreadPriority);
-constexpr ResultCode ERR_INVALID_POINTER(ErrorModule::Kernel, ErrCodes::InvalidPointer);
-constexpr ResultCode ERR_NOT_FOUND(ErrorModule::Kernel, ErrCodes::NoSuchEntry);
-constexpr ResultCode RESULT_TIMEOUT(ErrorModule::Kernel, ErrCodes::Timeout);
+constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
+constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
+constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
+constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
+constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
+constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
+constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
+constexpr ResultCode ERR_INVALID_MEMORY_RANGE{ErrorModule::Kernel, 110};
+constexpr ResultCode ERR_INVALID_PROCESSOR_ID{ErrorModule::Kernel, 113};
+constexpr ResultCode ERR_INVALID_THREAD_PRIORITY{ErrorModule::Kernel, 112};
+constexpr ResultCode ERR_INVALID_HANDLE{ErrorModule::Kernel, 114};
+constexpr ResultCode ERR_INVALID_POINTER{ErrorModule::Kernel, 115};
+constexpr ResultCode ERR_INVALID_COMBINATION{ErrorModule::Kernel, 116};
+constexpr ResultCode RESULT_TIMEOUT{ErrorModule::Kernel, 117};
+constexpr ResultCode ERR_SYNCHRONIZATION_CANCELED{ErrorModule::Kernel, 118};
+constexpr ResultCode ERR_OUT_OF_RANGE{ErrorModule::Kernel, 119};
+constexpr ResultCode ERR_INVALID_ENUM_VALUE{ErrorModule::Kernel, 120};
+constexpr ResultCode ERR_NOT_FOUND{ErrorModule::Kernel, 121};
+constexpr ResultCode ERR_BUSY{ErrorModule::Kernel, 122};
+constexpr ResultCode ERR_SESSION_CLOSED_BY_REMOTE{ErrorModule::Kernel, 123};
+constexpr ResultCode ERR_INVALID_STATE{ErrorModule::Kernel, 125};
+constexpr ResultCode ERR_RESERVED_VALUE{ErrorModule::Kernel, 126};
+constexpr ResultCode ERR_RESOURCE_LIMIT_EXCEEDED{ErrorModule::Kernel, 132};
} // namespace Kernel
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index 5ee5c05e3..c8acde5b1 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -12,12 +12,23 @@
#include "core/hle/kernel/thread.h"
namespace Kernel {
+namespace {
+constexpr u16 GetSlot(Handle handle) {
+ return handle >> 15;
+}
+
+constexpr u16 GetGeneration(Handle handle) {
+ return handle & 0x7FFF;
+}
+} // Anonymous namespace
HandleTable::HandleTable() {
next_generation = 1;
Clear();
}
+HandleTable::~HandleTable() = default;
+
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
DEBUG_ASSERT(obj != nullptr);
@@ -31,9 +42,10 @@ ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
u16 generation = next_generation++;
// Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
- // CTR-OS doesn't use generation 0, so skip straight to 1.
- if (next_generation >= (1 << 15))
+ // Horizon OS uses zero to represent an invalid handle, so skip to 1.
+ if (next_generation >= (1 << 15)) {
next_generation = 1;
+ }
generations[slot] = generation;
objects[slot] = std::move(obj);
diff --git a/src/core/hle/kernel/handle_table.h b/src/core/hle/kernel/handle_table.h
index 9e2f33e8a..89a3bc740 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -13,6 +13,7 @@
namespace Kernel {
enum KernelHandle : Handle {
+ InvalidHandle = 0,
CurrentThread = 0xFFFF8000,
CurrentProcess = 0xFFFF8001,
};
@@ -42,7 +43,11 @@ enum KernelHandle : Handle {
*/
class HandleTable final : NonCopyable {
public:
+ /// This is the maximum limit of handles allowed per process in Horizon
+ static constexpr std::size_t MAX_COUNT = 1024;
+
HandleTable();
+ ~HandleTable();
/**
* Allocates a handle for the given object.
@@ -89,19 +94,6 @@ public:
void Clear();
private:
- /**
- * This is the maximum limit of handles allowed per process in CTR-OS. It can be further
- * reduced by ExHeader values, but this is not emulated here.
- */
- static const std::size_t MAX_COUNT = 4096;
-
- static u16 GetSlot(Handle handle) {
- return handle >> 15;
- }
- static u16 GetGeneration(Handle handle) {
- return handle & 0x7FFF;
- }
-
/// Stores the Object referenced by the handle or null if the slot is empty.
std::array<SharedPtr<Object>, MAX_COUNT> objects;
diff --git a/src/core/hle/kernel/hle_ipc.cpp b/src/core/hle/kernel/hle_ipc.cpp
index 68d5376cb..5dd855db8 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -15,17 +15,23 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/server_session.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
namespace Kernel {
+SessionRequestHandler::SessionRequestHandler() = default;
+
+SessionRequestHandler::~SessionRequestHandler() = default;
+
void SessionRequestHandler::ClientConnected(SharedPtr<ServerSession> server_session) {
server_session->SetHleHandler(shared_from_this());
connected_sessions.push_back(std::move(server_session));
@@ -36,11 +42,9 @@ void SessionRequestHandler::ClientDisconnected(const SharedPtr<ServerSession>& s
boost::range::remove_erase(connected_sessions, server_session);
}
-SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
- const std::string& reason, u64 timeout,
- WakeupCallback&& callback,
- Kernel::SharedPtr<Kernel::Event> event) {
-
+SharedPtr<WritableEvent> HLERequestContext::SleepClientThread(
+ SharedPtr<Thread> thread, const std::string& reason, u64 timeout, WakeupCallback&& callback,
+ SharedPtr<WritableEvent> writable_event) {
// Put the client thread to sleep until the wait event is signaled or the timeout expires.
thread->SetWakeupCallback([context = *this, callback](
ThreadWakeupReason reason, SharedPtr<Thread> thread,
@@ -51,23 +55,25 @@ SharedPtr<Event> HLERequestContext::SleepClientThread(SharedPtr<Thread> thread,
return true;
});
- if (!event) {
+ auto& kernel = Core::System::GetInstance().Kernel();
+ if (!writable_event) {
// Create event if not provided
- auto& kernel = Core::System::GetInstance().Kernel();
- event =
- Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "HLE Pause Event: " + reason);
+ const auto pair = WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "HLE Pause Event: " + reason);
+ writable_event = pair.writable;
}
- event->Clear();
+ const auto readable_event{writable_event->GetReadableEvent()};
+ writable_event->Clear();
thread->SetStatus(ThreadStatus::WaitHLEEvent);
- thread->SetWaitObjects({event});
- event->AddWaitingThread(thread);
+ thread->SetWaitObjects({readable_event});
+ readable_event->AddWaitingThread(thread);
if (timeout > 0) {
thread->WakeAfterDelay(timeout);
}
- return event;
+ return writable_event;
}
HLERequestContext::HLERequestContext(SharedPtr<Kernel::ServerSession> server_session)
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index f01491daa..cb1c5aff3 100644
--- a/src/core/hle/kernel/hle_ipc.h
+++ b/src/core/hle/kernel/hle_ipc.h
@@ -14,8 +14,6 @@
#include "common/swap.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/server_session.h"
-#include "core/hle/kernel/thread.h"
namespace Service {
class ServiceFrameworkBase;
@@ -24,10 +22,15 @@ class ServiceFrameworkBase;
namespace Kernel {
class Domain;
-class Event;
class HandleTable;
class HLERequestContext;
class Process;
+class ServerSession;
+class Thread;
+class ReadableEvent;
+class WritableEvent;
+
+enum class ThreadWakeupReason;
/**
* Interface implemented by HLE Session handlers.
@@ -36,7 +39,8 @@ class Process;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
- virtual ~SessionRequestHandler() = default;
+ SessionRequestHandler();
+ virtual ~SessionRequestHandler();
/**
* Handles a sync request from the emulated application.
@@ -119,12 +123,13 @@ public:
* @param callback Callback to be invoked when the thread is resumed. This callback must write
* the entire command response once again, regardless of the state of it before this function
* was called.
- * @param event Event to use to wake up the thread. If unspecified, an event will be created.
+ * @param writable_event Event to use to wake up the thread. If unspecified, an event will be
+ * created.
* @returns Event that when signaled will resume the thread and call the callback function.
*/
- SharedPtr<Event> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
- u64 timeout, WakeupCallback&& callback,
- Kernel::SharedPtr<Kernel::Event> event = nullptr);
+ SharedPtr<WritableEvent> SleepClientThread(SharedPtr<Thread> thread, const std::string& reason,
+ u64 timeout, WakeupCallback&& callback,
+ SharedPtr<WritableEvent> writable_event = nullptr);
/// Populates this context with data from the requesting process/thread.
ResultCode PopulateFromIncomingCommandBuffer(const HandleTable& handle_table,
@@ -161,8 +166,12 @@ public:
return buffer_c_desciptors;
}
- const std::shared_ptr<IPC::DomainMessageHeader>& GetDomainMessageHeader() const {
- return domain_message_header;
+ const IPC::DomainMessageHeader* GetDomainMessageHeader() const {
+ return domain_message_header.get();
+ }
+
+ bool HasDomainMessageHeader() const {
+ return domain_message_header != nullptr;
}
/// Helper function to read a buffer using the appropriate buffer descriptor
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 4b6b32dd5..67674cd47 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -2,7 +2,6 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <array>
#include <atomic>
#include <memory>
#include <mutex>
@@ -32,7 +31,7 @@ namespace Kernel {
*/
static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_late) {
const auto proper_handle = static_cast<Handle>(thread_handle);
- auto& system = Core::System::GetInstance();
+ const auto& system = Core::System::GetInstance();
// Lock the global kernel mutex when we enter the kernel HLE.
std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
@@ -90,7 +89,7 @@ static void ThreadWakeupCallback(u64 thread_handle, [[maybe_unused]] int cycles_
/// The timer callback event, called when a timer is fired
static void TimerCallback(u64 timer_handle, int cycles_late) {
const auto proper_handle = static_cast<Handle>(timer_handle);
- auto& system = Core::System::GetInstance();
+ const auto& system = Core::System::GetInstance();
SharedPtr<Timer> timer = system.Kernel().RetrieveTimerFromCallbackHandleTable(proper_handle);
if (timer == nullptr) {
@@ -105,20 +104,20 @@ struct KernelCore::Impl {
void Initialize(KernelCore& kernel) {
Shutdown();
- InitializeResourceLimits(kernel);
+ InitializeSystemResourceLimit(kernel);
InitializeThreads();
InitializeTimers();
}
void Shutdown() {
next_object_id = 0;
- next_process_id = 10;
+ next_process_id = Process::ProcessIDMin;
next_thread_id = 1;
process_list.clear();
current_process = nullptr;
- resource_limits.fill(nullptr);
+ system_resource_limit = nullptr;
thread_wakeup_callback_handle_table.Clear();
thread_wakeup_event_type = nullptr;
@@ -129,63 +128,17 @@ struct KernelCore::Impl {
named_ports.clear();
}
- void InitializeResourceLimits(KernelCore& kernel) {
- // Create the four resource limits that the system uses
- // Create the APPLICATION resource limit
- SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
- resource_limit->max_priority = 0x18;
- resource_limit->max_commit = 0x4000000;
- resource_limit->max_threads = 0x20;
- resource_limit->max_events = 0x20;
- resource_limit->max_mutexes = 0x20;
- resource_limit->max_semaphores = 0x8;
- resource_limit->max_timers = 0x8;
- resource_limit->max_shared_mems = 0x10;
- resource_limit->max_address_arbiters = 0x2;
- resource_limit->max_cpu_time = 0x1E;
- resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
-
- // Create the SYS_APPLET resource limit
- resource_limit = ResourceLimit::Create(kernel, "System Applets");
- resource_limit->max_priority = 0x4;
- resource_limit->max_commit = 0x5E00000;
- resource_limit->max_threads = 0x1D;
- resource_limit->max_events = 0xB;
- resource_limit->max_mutexes = 0x8;
- resource_limit->max_semaphores = 0x4;
- resource_limit->max_timers = 0x4;
- resource_limit->max_shared_mems = 0x8;
- resource_limit->max_address_arbiters = 0x3;
- resource_limit->max_cpu_time = 0x2710;
- resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
-
- // Create the LIB_APPLET resource limit
- resource_limit = ResourceLimit::Create(kernel, "Library Applets");
- resource_limit->max_priority = 0x4;
- resource_limit->max_commit = 0x600000;
- resource_limit->max_threads = 0xE;
- resource_limit->max_events = 0x8;
- resource_limit->max_mutexes = 0x8;
- resource_limit->max_semaphores = 0x4;
- resource_limit->max_timers = 0x4;
- resource_limit->max_shared_mems = 0x8;
- resource_limit->max_address_arbiters = 0x1;
- resource_limit->max_cpu_time = 0x2710;
- resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
-
- // Create the OTHER resource limit
- resource_limit = ResourceLimit::Create(kernel, "Others");
- resource_limit->max_priority = 0x4;
- resource_limit->max_commit = 0x2180000;
- resource_limit->max_threads = 0xE1;
- resource_limit->max_events = 0x108;
- resource_limit->max_mutexes = 0x25;
- resource_limit->max_semaphores = 0x43;
- resource_limit->max_timers = 0x2C;
- resource_limit->max_shared_mems = 0x1F;
- resource_limit->max_address_arbiters = 0x2D;
- resource_limit->max_cpu_time = 0x3E8;
- resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
+ // Creates the default system resource limit
+ void InitializeSystemResourceLimit(KernelCore& kernel) {
+ system_resource_limit = ResourceLimit::Create(kernel, "System");
+
+ // If setting the default system values fails, then something seriously wrong has occurred.
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
+ .IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
+ ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
}
void InitializeThreads() {
@@ -199,16 +152,14 @@ struct KernelCore::Impl {
}
std::atomic<u32> next_object_id{0};
- // TODO(Subv): Start the process ids from 10 for now, as lower PIDs are
- // reserved for low-level services
- std::atomic<u32> next_process_id{10};
- std::atomic<u32> next_thread_id{1};
+ std::atomic<u64> next_process_id{Process::ProcessIDMin};
+ std::atomic<u64> next_thread_id{1};
// Lists all processes that exist in the current session.
std::vector<SharedPtr<Process>> process_list;
Process* current_process = nullptr;
- std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
+ SharedPtr<ResourceLimit> system_resource_limit;
/// The event type of the generic timer callback event
CoreTiming::EventType* timer_callback_event_type = nullptr;
@@ -239,9 +190,8 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
-SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
- ResourceLimitCategory category) const {
- return impl->resource_limits.at(static_cast<std::size_t>(category));
+SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
+ return impl->system_resource_limit;
}
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
@@ -289,11 +239,11 @@ u32 KernelCore::CreateNewObjectID() {
return impl->next_object_id++;
}
-u32 KernelCore::CreateNewThreadID() {
+u64 KernelCore::CreateNewThreadID() {
return impl->next_thread_id++;
}
-u32 KernelCore::CreateNewProcessID() {
+u64 KernelCore::CreateNewProcessID() {
return impl->next_process_id++;
}
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index 7f822d524..58c9d108b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -24,8 +24,6 @@ class ResourceLimit;
class Thread;
class Timer;
-enum class ResourceLimitCategory : u8;
-
/// Represents a single instance of the kernel.
class KernelCore {
private:
@@ -47,8 +45,8 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
- /// Retrieves a shared pointer to a ResourceLimit identified by the given category.
- SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
+ /// Retrieves a shared pointer to the system resource limit instance.
+ SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
@@ -90,10 +88,10 @@ private:
u32 CreateNewObjectID();
/// Creates a new process ID, incrementing the internal process ID counter;
- u32 CreateNewProcessID();
+ u64 CreateNewProcessID();
/// Creates a new thread ID, incrementing the internal thread ID counter.
- u32 CreateNewThreadID();
+ u64 CreateNewThreadID();
/// Creates a timer callback handle for the given timer.
ResultVal<Handle> CreateTimerCallbackHandle(const SharedPtr<Timer>& timer);
diff --git a/src/core/hle/kernel/mutex.cpp b/src/core/hle/kernel/mutex.cpp
index dd541ffcc..0743670ad 100644
--- a/src/core/hle/kernel/mutex.cpp
+++ b/src/core/hle/kernel/mutex.cpp
@@ -6,8 +6,6 @@
#include <utility>
#include <vector>
-#include <boost/range/algorithm_ext/erase.hpp>
-
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/errors.h"
diff --git a/src/core/hle/kernel/object.cpp b/src/core/hle/kernel/object.cpp
index d87a62bb9..806078638 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -13,16 +13,17 @@ Object::~Object() = default;
bool Object::IsWaitable() const {
switch (GetHandleType()) {
- case HandleType::Event:
+ case HandleType::ReadableEvent:
case HandleType::Thread:
+ case HandleType::Process:
case HandleType::Timer:
case HandleType::ServerPort:
case HandleType::ServerSession:
return true;
case HandleType::Unknown:
+ case HandleType::WritableEvent:
case HandleType::SharedMemory:
- case HandleType::Process:
case HandleType::AddressArbiter:
case HandleType::ResourceLimit:
case HandleType::ClientPort:
@@ -31,6 +32,7 @@ bool Object::IsWaitable() const {
}
UNREACHABLE();
+ return false;
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/object.h b/src/core/hle/kernel/object.h
index c9f4d0bb3..f1606a204 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -19,7 +19,8 @@ using Handle = u32;
enum class HandleType : u32 {
Unknown,
- Event,
+ WritableEvent,
+ ReadableEvent,
SharedMemory,
Thread,
Process,
@@ -33,9 +34,9 @@ enum class HandleType : u32 {
};
enum class ResetType {
- OneShot,
- Sticky,
- Pulse,
+ OneShot, ///< Reset automatically on object acquisition
+ Sticky, ///< Never reset automatically
+ Pulse, ///< Reset automatically on wakeup
};
class Object : NonCopyable {
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 420218d59..c5aa19afa 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,8 +4,8 @@
#include <algorithm>
#include <memory>
+#include <random>
#include "common/assert.h"
-#include "common/common_funcs.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/file_sys/program_metadata.h"
@@ -17,8 +17,38 @@
#include "core/hle/kernel/thread.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/memory.h"
+#include "core/settings.h"
namespace Kernel {
+namespace {
+/**
+ * Sets up the primary application thread
+ *
+ * @param owner_process The parent process for the main thread
+ * @param kernel The kernel instance to create the main thread under.
+ * @param entry_point The address at which the thread should start execution
+ * @param priority The priority to give the main thread
+ */
+void SetupMainThread(Process& owner_process, KernelCore& kernel, VAddr entry_point, u32 priority) {
+ // Setup page table so we can write to memory
+ SetCurrentPageTable(&owner_process.VMManager().page_table);
+
+ // Initialize new "main" thread
+ const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
+ auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0,
+ owner_process.GetIdealCore(), stack_top, owner_process);
+
+ SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
+
+ // Register 1 must be a handle to the main thread
+ const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
+ thread->SetGuestHandle(guest_handle);
+ thread->GetContext().cpu_registers[1] = guest_handle;
+
+ // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
+ thread->ResumeFromWait();
+}
+} // Anonymous namespace
CodeSet::CodeSet() = default;
CodeSet::~CodeSet() = default;
@@ -27,94 +57,49 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
SharedPtr<Process> process(new Process(kernel));
process->name = std::move(name);
- process->flags.raw = 0;
- process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
- process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
+ process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();
- process->svc_access_mask.set();
+ process->capabilities.InitializeForMetadatalessProcess();
+
+ std::mt19937 rng(Settings::values.rng_seed.value_or(0));
+ std::uniform_int_distribution<u64> distribution;
+ std::generate(process->random_entropy.begin(), process->random_entropy.end(),
+ [&] { return distribution(rng); });
kernel.AppendNewProcess(process);
return process;
}
-void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
- program_id = metadata.GetTitleID();
- is_64bit_process = metadata.Is64BitProgram();
- vm_manager.Reset(metadata.GetAddressSpaceType());
+SharedPtr<ResourceLimit> Process::GetResourceLimit() const {
+ return resource_limit;
}
-void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) {
- for (std::size_t i = 0; i < len; ++i) {
- u32 descriptor = kernel_caps[i];
- u32 type = descriptor >> 20;
-
- if (descriptor == 0xFFFFFFFF) {
- // Unused descriptor entry
- continue;
- } else if ((type & 0xF00) == 0xE00) { // 0x0FFF
- // Allowed interrupts list
- LOG_WARNING(Loader, "ExHeader allowed interrupts list ignored");
- } else if ((type & 0xF80) == 0xF00) { // 0x07FF
- // Allowed syscalls mask
- unsigned int index = ((descriptor >> 24) & 7) * 24;
- u32 bits = descriptor & 0xFFFFFF;
-
- while (bits && index < svc_access_mask.size()) {
- svc_access_mask.set(index, bits & 1);
- ++index;
- bits >>= 1;
- }
- } else if ((type & 0xFF0) == 0xFE0) { // 0x00FF
- // Handle table size
- handle_table_size = descriptor & 0x3FF;
- } else if ((type & 0xFF8) == 0xFF0) { // 0x007F
- // Misc. flags
- flags.raw = descriptor & 0xFFFF;
- } else if ((type & 0xFFE) == 0xFF8) { // 0x001F
- // Mapped memory range
- if (i + 1 >= len || ((kernel_caps[i + 1] >> 20) & 0xFFE) != 0xFF8) {
- LOG_WARNING(Loader, "Incomplete exheader memory range descriptor ignored.");
- continue;
- }
- u32 end_desc = kernel_caps[i + 1];
- ++i; // Skip over the second descriptor on the next iteration
+ResultCode Process::ClearSignalState() {
+ if (status == ProcessStatus::Exited) {
+ LOG_ERROR(Kernel, "called on a terminated process instance.");
+ return ERR_INVALID_STATE;
+ }
- AddressMapping mapping;
- mapping.address = descriptor << 12;
- VAddr end_address = end_desc << 12;
+ if (!is_signaled) {
+ LOG_ERROR(Kernel, "called on a process instance that isn't signaled.");
+ return ERR_INVALID_STATE;
+ }
- if (mapping.address < end_address) {
- mapping.size = end_address - mapping.address;
- } else {
- mapping.size = 0;
- }
+ is_signaled = false;
+ return RESULT_SUCCESS;
+}
- mapping.read_only = (descriptor & (1 << 20)) != 0;
- mapping.unk_flag = (end_desc & (1 << 20)) != 0;
-
- address_mappings.push_back(mapping);
- } else if ((type & 0xFFF) == 0xFFE) { // 0x000F
- // Mapped memory page
- AddressMapping mapping;
- mapping.address = descriptor << 12;
- mapping.size = Memory::PAGE_SIZE;
- mapping.read_only = false;
- mapping.unk_flag = false;
-
- address_mappings.push_back(mapping);
- } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF
- // Kernel version
- kernel_version = descriptor & 0xFFFF;
-
- int minor = kernel_version & 0xFF;
- int major = (kernel_version >> 8) & 0xFF;
- LOG_INFO(Loader, "ExHeader kernel version: {}.{}", major, minor);
- } else {
- LOG_ERROR(Loader, "Unhandled kernel caps descriptor: 0x{:08X}", descriptor);
- }
- }
+ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
+ program_id = metadata.GetTitleID();
+ ideal_core = metadata.GetMainThreadCore();
+ is_64bit_process = metadata.Is64BitProgram();
+
+ vm_manager.Reset(metadata.GetAddressSpaceType());
+
+ const auto& caps = metadata.GetKernelCapabilities();
+ return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
}
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
@@ -124,17 +109,17 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
vm_manager
.MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size,
std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size,
- MemoryState::Mapped)
+ MemoryState::Stack)
.Unwrap();
vm_manager.LogLayout();
- status = ProcessStatus::Running;
+ ChangeStatus(ProcessStatus::Running);
- Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
+ SetupMainThread(*this, kernel, entry_point, main_thread_priority);
}
void Process::PrepareForTermination() {
- status = ProcessStatus::Exited;
+ ChangeStatus(ProcessStatus::Exiting);
const auto stop_threads = [this](const std::vector<SharedPtr<Thread>>& thread_list) {
for (auto& thread : thread_list) {
@@ -158,6 +143,8 @@ void Process::PrepareForTermination() {
stop_threads(system.Scheduler(1).GetThreadList());
stop_threads(system.Scheduler(2).GetThreadList());
stop_threads(system.Scheduler(3).GetThreadList());
+
+ ChangeStatus(ProcessStatus::Exited);
}
/**
@@ -240,91 +227,25 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
}
-ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
- if (target < vm_manager.GetHeapRegionBaseAddress() ||
- target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
- return ERR_INVALID_ADDRESS;
- }
-
- if (heap_memory == nullptr) {
- // Initialize heap
- heap_memory = std::make_shared<std::vector<u8>>();
- heap_start = heap_end = target;
- } else {
- vm_manager.UnmapRange(heap_start, heap_end - heap_start);
- }
-
- // If necessary, expand backing vector to cover new heap extents.
- if (target < heap_start) {
- heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
- heap_start = target;
- vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
- }
- if (target + size > heap_end) {
- heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
- heap_end = target + size;
- vm_manager.RefreshMemoryBlockMappings(heap_memory.get());
- }
- ASSERT(heap_end - heap_start == heap_memory->size());
-
- CASCADE_RESULT(auto vma, vm_manager.MapMemoryBlock(target, heap_memory, target - heap_start,
- size, MemoryState::Heap));
- vm_manager.Reprotect(vma, perms);
-
- heap_used = size;
+Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
+Kernel::Process::~Process() {}
- return MakeResult<VAddr>(heap_end - size);
+void Process::Acquire(Thread* thread) {
+ ASSERT_MSG(!ShouldWait(thread), "Object unavailable!");
}
-ResultCode Process::HeapFree(VAddr target, u32 size) {
- if (target < vm_manager.GetHeapRegionBaseAddress() ||
- target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) {
- return ERR_INVALID_ADDRESS;
- }
-
- if (size == 0) {
- return RESULT_SUCCESS;
- }
-
- ResultCode result = vm_manager.UnmapRange(target, size);
- if (result.IsError())
- return result;
-
- heap_used -= size;
-
- return RESULT_SUCCESS;
+bool Process::ShouldWait(Thread* thread) const {
+ return !is_signaled;
}
-ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
- auto vma = vm_manager.FindVMA(src_addr);
-
- 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 = src_addr - vma->first;
- ASSERT_MSG(vma_offset + size <= vma->second.size,
- "Shared memory exceeds bounds of mapped block");
-
- const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
- std::size_t backing_block_offset = vma->second.offset + vma_offset;
-
- CASCADE_RESULT(auto new_vma,
- vm_manager.MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size,
- MemoryState::Mapped));
- // Protect mirror with permissions from old region
- vm_manager.Reprotect(new_vma, vma->second.permissions);
- // Remove permissions from old region
- vm_manager.Reprotect(vma, VMAPermission::None);
-
- return RESULT_SUCCESS;
-}
+void Process::ChangeStatus(ProcessStatus new_status) {
+ if (status == new_status) {
+ return;
+ }
-ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
- return vm_manager.UnmapRange(dst_addr, size);
+ status = new_status;
+ is_signaled = true;
+ WakeupAllWaitingThreads();
}
-Kernel::Process::Process(KernelCore& kernel) : Object{kernel} {}
-Kernel::Process::~Process() {}
-
} // namespace Kernel
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index 8d2616c79..dcc57ae9f 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -11,12 +11,12 @@
#include <string>
#include <vector>
#include <boost/container/static_vector.hpp>
-#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/hle/kernel/handle_table.h"
-#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/process_capability.h"
#include "core/hle/kernel/vm_manager.h"
+#include "core/hle/kernel/wait_object.h"
+#include "core/hle/result.h"
namespace FileSys {
class ProgramMetadata;
@@ -26,6 +26,7 @@ namespace Kernel {
class KernelCore;
class ResourceLimit;
+class Thread;
struct AddressMapping {
// Address and size must be page-aligned
@@ -41,24 +42,6 @@ enum class MemoryRegion : u16 {
BASE = 3,
};
-union ProcessFlags {
- u16 raw;
-
- BitField<0, 1, u16>
- allow_debug; ///< Allows other processes to attach to and debug this process.
- BitField<1, 1, u16> force_debug; ///< Allows this process to attach to processes even if they
- /// don't have allow_debug set.
- BitField<2, 1, u16> allow_nonalphanum;
- BitField<3, 1, u16> shared_page_writable; ///< Shared page is mapped with write permissions.
- BitField<4, 1, u16> privileged_priority; ///< Can use priority levels higher than 24.
- BitField<5, 1, u16> allow_main_args;
- BitField<6, 1, u16> shared_device_mem;
- BitField<7, 1, u16> runnable_on_sleep;
- BitField<8, 4, MemoryRegion>
- memory_region; ///< Default region for memory allocations for this process
- BitField<12, 1, u16> loaded_high; ///< Application loaded high (not at 0x00100000).
-};
-
/**
* Indicates the status of a Process instance.
*
@@ -117,8 +100,22 @@ struct CodeSet final {
VAddr entrypoint = 0;
};
-class Process final : public Object {
+class Process final : public WaitObject {
public:
+ enum : u64 {
+ /// Lowest allowed process ID for a kernel initial process.
+ InitialKIPIDMin = 1,
+ /// Highest allowed process ID for a kernel initial process.
+ InitialKIPIDMax = 80,
+
+ /// Lowest allowed process ID for a userland process.
+ ProcessIDMin = 81,
+ /// Highest allowed process ID for a userland process.
+ ProcessIDMax = 0xFFFFFFFFFFFFFFFF,
+ };
+
+ static constexpr std::size_t RANDOM_ENTROPY_SIZE = 4;
+
static SharedPtr<Process> Create(KernelCore& kernel, std::string&& name);
std::string GetTypeName() const override {
@@ -159,7 +156,7 @@ public:
}
/// Gets the unique ID that identifies this particular process.
- u32 GetProcessID() const {
+ u64 GetProcessID() const {
return process_id;
}
@@ -169,28 +166,21 @@ public:
}
/// Gets the resource limit descriptor for this process
- ResourceLimit& GetResourceLimit() {
- return *resource_limit;
- }
-
- /// Gets the resource limit descriptor for this process
- const ResourceLimit& GetResourceLimit() const {
- return *resource_limit;
- }
+ SharedPtr<ResourceLimit> GetResourceLimit() const;
- /// Gets the default CPU ID for this process
- u8 GetDefaultProcessorID() const {
- return ideal_processor;
+ /// Gets the ideal CPU core ID for this process
+ u8 GetIdealCore() const {
+ return ideal_core;
}
- /// Gets the bitmask of allowed CPUs that this process' threads can run on.
- u32 GetAllowedProcessorMask() const {
- return allowed_processor_mask;
+ /// Gets the bitmask of allowed cores that this process' threads can run on.
+ u64 GetCoreMask() const {
+ return capabilities.GetCoreMask();
}
/// Gets the bitmask of allowed thread priorities.
- u32 GetAllowedThreadPriorityMask() const {
- return allowed_thread_priority_mask;
+ u64 GetPriorityMask() const {
+ return capabilities.GetPriorityMask();
}
u32 IsVirtualMemoryEnabled() const {
@@ -212,19 +202,31 @@ public:
total_process_running_time_ticks += ticks;
}
+ /// Gets 8 bytes of random data for svcGetInfo RandomEntropy
+ u64 GetRandomEntropy(std::size_t index) const {
+ return random_entropy.at(index);
+ }
+
+ /// Clears the signaled state of the process if and only if it's signaled.
+ ///
+ /// @pre The process must not be already terminated. If this is called on a
+ /// terminated process, then ERR_INVALID_STATE will be returned.
+ ///
+ /// @pre The process must be in a signaled state. If this is called on a
+ /// process instance that is not signaled, ERR_INVALID_STATE will be
+ /// returned.
+ ResultCode ClearSignalState();
+
/**
* Loads process-specifics configuration info with metadata provided
* by an executable.
*
- * @param metadata The provided metadata to load process specific info.
- */
- void LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
-
- /**
- * Parses a list of kernel capability descriptors (as found in the ExHeader) and applies them
- * to this process.
+ * @param metadata The provided metadata to load process specific info from.
+ *
+ * @returns RESULT_SUCCESS if all relevant metadata was able to be
+ * loaded and parsed. Otherwise, an error code is returned.
*/
- void ParseKernelCaps(const u32* kernel_caps, std::size_t len);
+ ResultCode LoadFromMetadata(const FileSys::ProgramMetadata& metadata);
/**
* Applies address space changes and launches the process main thread.
@@ -240,7 +242,7 @@ public:
void LoadModule(CodeSet module_, VAddr base_addr);
///////////////////////////////////////////////////////////////////////////////////////////////
- // Memory Management
+ // Thread-local storage management
// Marks the next available region as used and returns the address of the slot.
VAddr MarkNextAvailableTLSSlotAsUsed(Thread& thread);
@@ -248,17 +250,21 @@ public:
// Frees a used TLS slot identified by the given address
void FreeTLSSlot(VAddr tls_address);
- ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
- ResultCode HeapFree(VAddr target, u32 size);
-
- ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size);
-
- ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
-
private:
explicit Process(KernelCore& kernel);
~Process() override;
+ /// Checks if the specified thread should wait until this process is available.
+ bool ShouldWait(Thread* thread) const override;
+
+ /// Acquires/locks this process for the specified thread if it's available.
+ void Acquire(Thread* thread) override;
+
+ /// Changes the process status. If the status is different
+ /// from the current process status, then this will trigger
+ /// a process signal.
+ void ChangeStatus(ProcessStatus new_status);
+
/// Memory manager for this process.
Kernel::VMManager vm_manager;
@@ -266,43 +272,18 @@ private:
ProcessStatus status;
/// The ID of this process
- u32 process_id = 0;
+ u64 process_id = 0;
/// Title ID corresponding to the process
- u64 program_id;
+ u64 program_id = 0;
/// Resource limit descriptor for this process
SharedPtr<ResourceLimit> resource_limit;
- /// The process may only call SVCs which have the corresponding bit set.
- std::bitset<0x80> svc_access_mask;
- /// Maximum size of the handle table for the process.
- u32 handle_table_size = 0x200;
- /// Special memory ranges mapped into this processes address space. This is used to give
- /// processes access to specific I/O regions and device memory.
- boost::container::static_vector<AddressMapping, 8> address_mappings;
- ProcessFlags flags;
- /// Kernel compatibility version for this process
- u16 kernel_version = 0;
- /// The default CPU for this process, threads are scheduled on this cpu by default.
- u8 ideal_processor = 0;
- /// Bitmask of allowed CPUs that this process' threads can run on. TODO(Subv): Actually parse
- /// this value from the process header.
- u32 allowed_processor_mask = THREADPROCESSORID_DEFAULT_MASK;
- u32 allowed_thread_priority_mask = 0xFFFFFFFF;
+ /// The ideal CPU core for this process, threads are scheduled on this core by default.
+ u8 ideal_core = 0;
u32 is_virtual_address_memory_enabled = 0;
- // Memory used to back the allocations in the regular heap. A single vector is used to cover
- // the entire virtual address space extents that bound the allocations, including any holes.
- // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
- // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
- std::shared_ptr<std::vector<u8>> heap_memory;
-
- // The left/right bounds of the address space covered by heap_memory.
- VAddr heap_start = 0;
- VAddr heap_end = 0;
- u64 heap_used = 0;
-
/// The Thread Local Storage area is allocated as processes create threads,
/// each TLS area is 0x200 bytes, so one page (0x1000) is split up in 8 parts, and each part
/// holds the TLS for a specific thread. This vector contains which parts are in use for each
@@ -310,17 +291,27 @@ private:
/// This vector will grow as more pages are allocated for new threads.
std::vector<std::bitset<8>> tls_slots;
+ /// Contains the parsed process capability descriptors.
+ ProcessCapabilities capabilities;
+
/// Whether or not this process is AArch64, or AArch32.
/// By default, we currently assume this is true, unless otherwise
/// specified by metadata provided to the process during loading.
bool is_64bit_process = true;
+ /// Whether or not this process is signaled. This occurs
+ /// upon the process changing to a different state.
+ bool is_signaled = false;
+
/// Total running time for the process in ticks.
u64 total_process_running_time_ticks = 0;
/// Per-process handle table for storing created object handles in.
HandleTable handle_table;
+ /// Random values for svcGetInfo RandomEntropy
+ std::array<u64, RANDOM_ENTROPY_SIZE> random_entropy;
+
std::string name;
};
diff --git a/src/core/hle/kernel/process_capability.cpp b/src/core/hle/kernel/process_capability.cpp
new file mode 100644
index 000000000..3a2164b25
--- /dev/null
+++ b/src/core/hle/kernel/process_capability.cpp
@@ -0,0 +1,355 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/bit_util.h"
+#include "core/hle/kernel/errors.h"
+#include "core/hle/kernel/handle_table.h"
+#include "core/hle/kernel/process_capability.h"
+#include "core/hle/kernel/vm_manager.h"
+
+namespace Kernel {
+namespace {
+
+// clang-format off
+
+// Shift offsets for kernel capability types.
+enum : u32 {
+ CapabilityOffset_PriorityAndCoreNum = 3,
+ CapabilityOffset_Syscall = 4,
+ CapabilityOffset_MapPhysical = 6,
+ CapabilityOffset_MapIO = 7,
+ CapabilityOffset_Interrupt = 11,
+ CapabilityOffset_ProgramType = 13,
+ CapabilityOffset_KernelVersion = 14,
+ CapabilityOffset_HandleTableSize = 15,
+ CapabilityOffset_Debug = 16,
+};
+
+// Combined mask of all parameters that may be initialized only once.
+constexpr u32 InitializeOnceMask = (1U << CapabilityOffset_PriorityAndCoreNum) |
+ (1U << CapabilityOffset_ProgramType) |
+ (1U << CapabilityOffset_KernelVersion) |
+ (1U << CapabilityOffset_HandleTableSize) |
+ (1U << CapabilityOffset_Debug);
+
+// Packed kernel version indicating 10.4.0
+constexpr u32 PackedKernelVersion = 0x520000;
+
+// Indicates possible types of capabilities that can be specified.
+enum class CapabilityType : u32 {
+ Unset = 0U,
+ PriorityAndCoreNum = (1U << CapabilityOffset_PriorityAndCoreNum) - 1,
+ Syscall = (1U << CapabilityOffset_Syscall) - 1,
+ MapPhysical = (1U << CapabilityOffset_MapPhysical) - 1,
+ MapIO = (1U << CapabilityOffset_MapIO) - 1,
+ Interrupt = (1U << CapabilityOffset_Interrupt) - 1,
+ ProgramType = (1U << CapabilityOffset_ProgramType) - 1,
+ KernelVersion = (1U << CapabilityOffset_KernelVersion) - 1,
+ HandleTableSize = (1U << CapabilityOffset_HandleTableSize) - 1,
+ Debug = (1U << CapabilityOffset_Debug) - 1,
+ Ignorable = 0xFFFFFFFFU,
+};
+
+// clang-format on
+
+constexpr CapabilityType GetCapabilityType(u32 value) {
+ return static_cast<CapabilityType>((~value & (value + 1)) - 1);
+}
+
+u32 GetFlagBitOffset(CapabilityType type) {
+ const auto value = static_cast<u32>(type);
+ return static_cast<u32>(Common::BitSize<u32>() - Common::CountLeadingZeroes32(value));
+}
+
+} // Anonymous namespace
+
+ResultCode ProcessCapabilities::InitializeForKernelProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ VMManager& vm_manager) {
+ Clear();
+
+ // Allow all cores and priorities.
+ core_mask = 0xF;
+ priority_mask = 0xFFFFFFFFFFFFFFFF;
+ kernel_version = PackedKernelVersion;
+
+ return ParseCapabilities(capabilities, num_capabilities, vm_manager);
+}
+
+ResultCode ProcessCapabilities::InitializeForUserProcess(const u32* capabilities,
+ std::size_t num_capabilities,
+ VMManager& vm_manager) {
+ Clear();
+
+ return ParseCapabilities(capabilities, num_capabilities, vm_manager);
+}
+
+void ProcessCapabilities::InitializeForMetadatalessProcess() {
+ // Allow all cores and priorities
+ core_mask = 0xF;
+ priority_mask = 0xFFFFFFFFFFFFFFFF;
+ kernel_version = PackedKernelVersion;
+
+ // Allow all system calls and interrupts.
+ svc_capabilities.set();
+ interrupt_capabilities.set();
+
+ // Allow using the maximum possible amount of handles
+ handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
+
+ // Allow all debugging capabilities.
+ is_debuggable = true;
+ can_force_debug = true;
+}
+
+ResultCode ProcessCapabilities::ParseCapabilities(const u32* capabilities,
+ std::size_t num_capabilities,
+ VMManager& vm_manager) {
+ u32 set_flags = 0;
+ u32 set_svc_bits = 0;
+
+ for (std::size_t i = 0; i < num_capabilities; ++i) {
+ const u32 descriptor = capabilities[i];
+ const auto type = GetCapabilityType(descriptor);
+
+ if (type == CapabilityType::MapPhysical) {
+ i++;
+
+ // The MapPhysical type uses two descriptor flags for its parameters.
+ // If there's only one, then there's a problem.
+ if (i >= num_capabilities) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ const auto size_flags = capabilities[i];
+ if (GetCapabilityType(size_flags) != CapabilityType::MapPhysical) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ const auto result = HandleMapPhysicalFlags(descriptor, size_flags, vm_manager);
+ if (result.IsError()) {
+ return result;
+ }
+ } else {
+ const auto result =
+ ParseSingleFlagCapability(set_flags, set_svc_bits, descriptor, vm_manager);
+ if (result.IsError()) {
+ return result;
+ }
+ }
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits,
+ u32 flag, VMManager& vm_manager) {
+ const auto type = GetCapabilityType(flag);
+
+ if (type == CapabilityType::Unset) {
+ return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ }
+
+ // Bail early on ignorable entries, as one would expect,
+ // ignorable descriptors can be ignored.
+ if (type == CapabilityType::Ignorable) {
+ return RESULT_SUCCESS;
+ }
+
+ // Ensure that the give flag hasn't already been initialized before.
+ // If it has been, then bail.
+ const u32 flag_length = GetFlagBitOffset(type);
+ const u32 set_flag = 1U << flag_length;
+ if ((set_flag & set_flags & InitializeOnceMask) != 0) {
+ return ERR_INVALID_COMBINATION;
+ }
+ set_flags |= set_flag;
+
+ switch (type) {
+ case CapabilityType::PriorityAndCoreNum:
+ return HandlePriorityCoreNumFlags(flag);
+ case CapabilityType::Syscall:
+ return HandleSyscallFlags(set_svc_bits, flag);
+ case CapabilityType::MapIO:
+ return HandleMapIOFlags(flag, vm_manager);
+ case CapabilityType::Interrupt:
+ return HandleInterruptFlags(flag);
+ case CapabilityType::ProgramType:
+ return HandleProgramTypeFlags(flag);
+ case CapabilityType::KernelVersion:
+ return HandleKernelVersionFlags(flag);
+ case CapabilityType::HandleTableSize:
+ return HandleHandleTableFlags(flag);
+ case CapabilityType::Debug:
+ return HandleDebugFlags(flag);
+ default:
+ break;
+ }
+
+ return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+}
+
+void ProcessCapabilities::Clear() {
+ svc_capabilities.reset();
+ interrupt_capabilities.reset();
+
+ core_mask = 0;
+ priority_mask = 0;
+
+ handle_table_size = 0;
+ kernel_version = 0;
+
+ program_type = ProgramType::SysModule;
+
+ is_debuggable = false;
+ can_force_debug = false;
+}
+
+ResultCode ProcessCapabilities::HandlePriorityCoreNumFlags(u32 flags) {
+ if (priority_mask != 0 || core_mask != 0) {
+ return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ }
+
+ const u32 core_num_min = (flags >> 16) & 0xFF;
+ const u32 core_num_max = (flags >> 24) & 0xFF;
+ if (core_num_min > core_num_max) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ const u32 priority_min = (flags >> 10) & 0x3F;
+ const u32 priority_max = (flags >> 4) & 0x3F;
+ if (priority_min > priority_max) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ // The switch only has 4 usable cores.
+ if (core_num_max >= 4) {
+ return ERR_INVALID_PROCESSOR_ID;
+ }
+
+ const auto make_mask = [](u64 min, u64 max) {
+ const u64 range = max - min + 1;
+ const u64 mask = (1ULL << range) - 1;
+
+ return mask << min;
+ };
+
+ core_mask = make_mask(core_num_min, core_num_max);
+ priority_mask = make_mask(priority_min, priority_max);
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleSyscallFlags(u32& set_svc_bits, u32 flags) {
+ const u32 index = flags >> 29;
+ const u32 svc_bit = 1U << index;
+
+ // If we've already set this svc before, bail.
+ if ((set_svc_bits & svc_bit) != 0) {
+ return ERR_INVALID_COMBINATION;
+ }
+ set_svc_bits |= svc_bit;
+
+ const u32 svc_mask = (flags >> 5) & 0xFFFFFF;
+ for (u32 i = 0; i < 24; ++i) {
+ const u32 svc_number = index * 24 + i;
+
+ if ((svc_mask & (1U << i)) == 0) {
+ continue;
+ }
+
+ if (svc_number >= svc_capabilities.size()) {
+ return ERR_OUT_OF_RANGE;
+ }
+
+ svc_capabilities[svc_number] = true;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleMapPhysicalFlags(u32 flags, u32 size_flags,
+ VMManager& vm_manager) {
+ // TODO(Lioncache): Implement once the memory manager can handle this.
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleMapIOFlags(u32 flags, VMManager& vm_manager) {
+ // TODO(Lioncache): Implement once the memory manager can handle this.
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleInterruptFlags(u32 flags) {
+ constexpr u32 interrupt_ignore_value = 0x3FF;
+ const u32 interrupt0 = (flags >> 12) & 0x3FF;
+ const u32 interrupt1 = (flags >> 22) & 0x3FF;
+
+ for (u32 interrupt : {interrupt0, interrupt1}) {
+ if (interrupt == interrupt_ignore_value) {
+ continue;
+ }
+
+ // NOTE:
+ // This should be checking a generic interrupt controller value
+ // as part of the calculation, however, given we don't currently
+ // emulate that, it's sufficient to mark every interrupt as defined.
+
+ if (interrupt >= interrupt_capabilities.size()) {
+ return ERR_OUT_OF_RANGE;
+ }
+
+ interrupt_capabilities[interrupt] = true;
+ }
+
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleProgramTypeFlags(u32 flags) {
+ const u32 reserved = flags >> 17;
+ if (reserved != 0) {
+ return ERR_RESERVED_VALUE;
+ }
+
+ program_type = static_cast<ProgramType>((flags >> 14) & 0b111);
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleKernelVersionFlags(u32 flags) {
+ // Yes, the internal member variable is checked in the actual kernel here.
+ // This might look odd for options that are only allowed to be initialized
+ // just once, however the kernel has a separate initialization function for
+ // kernel processes and userland processes. The kernel variant sets this
+ // member variable ahead of time.
+
+ const u32 major_version = kernel_version >> 19;
+
+ if (major_version != 0 || flags < 0x80000) {
+ return ERR_INVALID_CAPABILITY_DESCRIPTOR;
+ }
+
+ kernel_version = flags;
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
+ const u32 reserved = flags >> 26;
+ if (reserved != 0) {
+ return ERR_RESERVED_VALUE;
+ }
+
+ handle_table_size = (flags >> 16) & 0x3FF;
+ return RESULT_SUCCESS;
+}
+
+ResultCode ProcessCapabilities::HandleDebugFlags(u32 flags) {
+ const u32 reserved = flags >> 19;
+ if (reserved != 0) {
+ return ERR_RESERVED_VALUE;
+ }
+
+ is_debuggable = (flags & 0x20000) != 0;
+ can_force_debug = (flags & 0x40000) != 0;
+ return RESULT_SUCCESS;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/process_capability.h b/src/core/hle/kernel/process_capability.h
new file mode 100644
index 000000000..fbc8812a3
--- /dev/null
+++ b/src/core/hle/kernel/process_capability.h
@@ -0,0 +1,264 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <bitset>
+
+#include "common/common_types.h"
+
+union ResultCode;
+
+namespace Kernel {
+
+class VMManager;
+
+/// The possible types of programs that may be indicated
+/// by the program type capability descriptor.
+enum class ProgramType {
+ SysModule,
+ Application,
+ Applet,
+};
+
+/// Handles kernel capability descriptors that are provided by
+/// application metadata. These descriptors provide information
+/// that alters certain parameters for kernel process instance
+/// that will run said application (or applet).
+///
+/// Capabilities are a sequence of flag descriptors, that indicate various
+/// configurations and constraints for a particular process.
+///
+/// Flag types are indicated by a sequence of set low bits. E.g. the
+/// types are indicated with the low bits as follows (where x indicates "don't care"):
+///
+/// - Priority and core mask : 0bxxxxxxxxxxxx0111
+/// - Allowed service call mask: 0bxxxxxxxxxxx01111
+/// - Map physical memory : 0bxxxxxxxxx0111111
+/// - Map IO memory : 0bxxxxxxxx01111111
+/// - Interrupts : 0bxxxx011111111111
+/// - Application type : 0bxx01111111111111
+/// - Kernel version : 0bx011111111111111
+/// - Handle table size : 0b0111111111111111
+/// - Debugger flags : 0b1111111111111111
+///
+/// These are essentially a bit offset subtracted by 1 to create a mask.
+/// e.g. The first entry in the above list is simply bit 3 (value 8 -> 0b1000)
+/// subtracted by one (7 -> 0b0111)
+///
+/// An example of a bit layout (using the map physical layout):
+/// <example>
+/// The MapPhysical type indicates a sequence entry pair of:
+///
+/// [initial, memory_flags], where:
+///
+/// initial:
+/// bits:
+/// 7-24: Starting page to map memory at.
+/// 25 : Indicates if the memory should be mapped as read only.
+///
+/// memory_flags:
+/// bits:
+/// 7-20 : Number of pages to map
+/// 21-25: Seems to be reserved (still checked against though)
+/// 26 : Whether or not the memory being mapped is IO memory, or physical memory
+/// </example>
+///
+class ProcessCapabilities {
+public:
+ using InterruptCapabilities = std::bitset<1024>;
+ using SyscallCapabilities = std::bitset<128>;
+
+ ProcessCapabilities() = default;
+ ProcessCapabilities(const ProcessCapabilities&) = delete;
+ ProcessCapabilities(ProcessCapabilities&&) = default;
+
+ ProcessCapabilities& operator=(const ProcessCapabilities&) = delete;
+ ProcessCapabilities& operator=(ProcessCapabilities&&) = default;
+
+ /// Initializes this process capabilities instance for a kernel process.
+ ///
+ /// @param capabilities The capabilities to parse
+ /// @param num_capabilities The number of capabilities to parse.
+ /// @param vm_manager The memory manager to use for handling any mapping-related
+ /// operations (such as mapping IO memory, etc).
+ ///
+ /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
+ /// otherwise, an error code upon failure.
+ ///
+ ResultCode InitializeForKernelProcess(const u32* capabilities, std::size_t num_capabilities,
+ VMManager& vm_manager);
+
+ /// Initializes this process capabilities instance for a userland process.
+ ///
+ /// @param capabilities The capabilities to parse.
+ /// @param num_capabilities The total number of capabilities to parse.
+ /// @param vm_manager The memory manager to use for handling any mapping-related
+ /// operations (such as mapping IO memory, etc).
+ ///
+ /// @returns RESULT_SUCCESS if this capabilities instance was able to be initialized,
+ /// otherwise, an error code upon failure.
+ ///
+ ResultCode InitializeForUserProcess(const u32* capabilities, std::size_t num_capabilities,
+ VMManager& vm_manager);
+
+ /// Initializes this process capabilities instance for a process that does not
+ /// have any metadata to parse.
+ ///
+ /// This is necessary, as we allow running raw executables, and the internal
+ /// kernel process capabilities also determine what CPU cores the process is
+ /// allowed to run on, and what priorities are allowed for threads. It also
+ /// determines the max handle table size, what the program type is, whether or
+ /// not the process can be debugged, or whether it's possible for a process to
+ /// forcibly debug another process.
+ ///
+ /// Given the above, this essentially enables all capabilities across the board
+ /// for the process. It allows the process to:
+ ///
+ /// - Run on any core
+ /// - Use any thread priority
+ /// - Use the maximum amount of handles a process is allowed to.
+ /// - Be debuggable
+ /// - Forcibly debug other processes.
+ ///
+ /// Note that this is not a behavior that the kernel allows a process to do via
+ /// a single function like this. This is yuzu-specific behavior to handle
+ /// executables with no capability descriptors whatsoever to derive behavior from.
+ /// It being yuzu-specific is why this is also not the default behavior and not
+ /// done by default in the constructor.
+ ///
+ void InitializeForMetadatalessProcess();
+
+ /// Gets the allowable core mask
+ u64 GetCoreMask() const {
+ return core_mask;
+ }
+
+ /// Gets the allowable priority mask
+ u64 GetPriorityMask() const {
+ return priority_mask;
+ }
+
+ /// Gets the SVC access permission bits
+ const SyscallCapabilities& GetServiceCapabilities() const {
+ return svc_capabilities;
+ }
+
+ /// Gets the valid interrupt bits.
+ const InterruptCapabilities& GetInterruptCapabilities() const {
+ return interrupt_capabilities;
+ }
+
+ /// Gets the program type for this process.
+ ProgramType GetProgramType() const {
+ return program_type;
+ }
+
+ /// Gets the number of total allowable handles for the process' handle table.
+ u32 GetHandleTableSize() const {
+ return handle_table_size;
+ }
+
+ /// Gets the kernel version value.
+ u32 GetKernelVersion() const {
+ return kernel_version;
+ }
+
+ /// Whether or not this process can be debugged.
+ bool IsDebuggable() const {
+ return is_debuggable;
+ }
+
+ /// Whether or not this process can forcibly debug another
+ /// process, even if that process is not considered debuggable.
+ bool CanForceDebug() const {
+ return can_force_debug;
+ }
+
+private:
+ /// Attempts to parse a given sequence of capability descriptors.
+ ///
+ /// @param capabilities The sequence of capability descriptors to parse.
+ /// @param num_capabilities The number of descriptors within the given sequence.
+ /// @param vm_manager The memory manager that will perform any memory
+ /// mapping if necessary.
+ ///
+ /// @return RESULT_SUCCESS if no errors occur, otherwise an error code.
+ ///
+ ResultCode ParseCapabilities(const u32* capabilities, std::size_t num_capabilities,
+ VMManager& vm_manager);
+
+ /// Attempts to parse a capability descriptor that is only represented by a
+ /// single flag set.
+ ///
+ /// @param set_flags Running set of flags that are used to catch
+ /// flags being initialized more than once when they shouldn't be.
+ /// @param set_svc_bits Running set of bits representing the allowed supervisor calls mask.
+ /// @param flag The flag to attempt to parse.
+ /// @param vm_manager The memory manager that will perform any memory
+ /// mapping if necessary.
+ ///
+ /// @return RESULT_SUCCESS if no errors occurred, otherwise an error code.
+ ///
+ ResultCode ParseSingleFlagCapability(u32& set_flags, u32& set_svc_bits, u32 flag,
+ VMManager& vm_manager);
+
+ /// Clears the internal state of this process capability instance. Necessary,
+ /// to have a sane starting point due to us allowing running executables without
+ /// configuration metadata. We assume a process is not going to have metadata,
+ /// and if it turns out that the process does, in fact, have metadata, then
+ /// we attempt to parse it. Thus, we need this to reset data members back to
+ /// a good state.
+ ///
+ /// DO NOT ever make this a public member function. This isn't an invariant
+ /// anything external should depend upon (and if anything comes to rely on it,
+ /// you should immediately be questioning the design of that thing, not this
+ /// class. If the kernel itself can run without depending on behavior like that,
+ /// then so can yuzu).
+ ///
+ void Clear();
+
+ /// Handles flags related to the priority and core number capability flags.
+ ResultCode HandlePriorityCoreNumFlags(u32 flags);
+
+ /// Handles flags related to determining the allowable SVC mask.
+ ResultCode HandleSyscallFlags(u32& set_svc_bits, u32 flags);
+
+ /// Handles flags related to mapping physical memory pages.
+ ResultCode HandleMapPhysicalFlags(u32 flags, u32 size_flags, VMManager& vm_manager);
+
+ /// Handles flags related to mapping IO pages.
+ ResultCode HandleMapIOFlags(u32 flags, VMManager& vm_manager);
+
+ /// Handles flags related to the interrupt capability flags.
+ ResultCode HandleInterruptFlags(u32 flags);
+
+ /// Handles flags related to the program type.
+ ResultCode HandleProgramTypeFlags(u32 flags);
+
+ /// Handles flags related to the handle table size.
+ ResultCode HandleHandleTableFlags(u32 flags);
+
+ /// Handles flags related to the kernel version capability flags.
+ ResultCode HandleKernelVersionFlags(u32 flags);
+
+ /// Handles flags related to debug-specific capabilities.
+ ResultCode HandleDebugFlags(u32 flags);
+
+ SyscallCapabilities svc_capabilities;
+ InterruptCapabilities interrupt_capabilities;
+
+ u64 core_mask = 0;
+ u64 priority_mask = 0;
+
+ u32 handle_table_size = 0;
+ u32 kernel_version = 0;
+
+ ProgramType program_type = ProgramType::SysModule;
+
+ bool is_debuggable = false;
+ bool can_force_debug = false;
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/event.cpp b/src/core/hle/kernel/readable_event.cpp
index 8967e602e..ba01f495c 100644
--- a/src/core/hle/kernel/event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -4,46 +4,47 @@
#include <algorithm>
#include "common/assert.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/thread.h"
namespace Kernel {
-Event::Event(KernelCore& kernel) : WaitObject{kernel} {}
-Event::~Event() = default;
+ReadableEvent::ReadableEvent(KernelCore& kernel) : WaitObject{kernel} {}
+ReadableEvent::~ReadableEvent() = default;
-SharedPtr<Event> Event::Create(KernelCore& kernel, ResetType reset_type, std::string name) {
- SharedPtr<Event> evt(new Event(kernel));
-
- evt->signaled = false;
- evt->reset_type = reset_type;
- evt->name = std::move(name);
-
- return evt;
-}
-
-bool Event::ShouldWait(Thread* thread) const {
+bool ReadableEvent::ShouldWait(Thread* thread) const {
return !signaled;
}
-void Event::Acquire(Thread* thread) {
+void ReadableEvent::Acquire(Thread* thread) {
ASSERT_MSG(!ShouldWait(thread), "object unavailable!");
if (reset_type == ResetType::OneShot)
signaled = false;
}
-void Event::Signal() {
+void ReadableEvent::Signal() {
signaled = true;
WakeupAllWaitingThreads();
}
-void Event::Clear() {
+void ReadableEvent::Clear() {
signaled = false;
}
-void Event::WakeupAllWaitingThreads() {
+ResultCode ReadableEvent::Reset() {
+ if (!signaled) {
+ return ERR_INVALID_STATE;
+ }
+
+ Clear();
+
+ return RESULT_SUCCESS;
+}
+
+void ReadableEvent::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
if (reset_type == ResetType::Pulse)
diff --git a/src/core/hle/kernel/event.h b/src/core/hle/kernel/readable_event.h
index 27d6126b0..80b3b0aba 100644
--- a/src/core/hle/kernel/event.h
+++ b/src/core/hle/kernel/readable_event.h
@@ -4,56 +4,62 @@
#pragma once
-#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
+union ResultCode;
+
namespace Kernel {
class KernelCore;
+class WritableEvent;
+
+class ReadableEvent final : public WaitObject {
+ friend class WritableEvent;
-class Event final : public WaitObject {
public:
- /**
- * Creates an event
- * @param kernel The kernel instance to create this event under.
- * @param reset_type ResetType describing how to create event
- * @param name Optional name of event
- */
- static SharedPtr<Event> Create(KernelCore& kernel, ResetType reset_type,
- std::string name = "Unknown");
+ ~ReadableEvent() override;
std::string GetTypeName() const override {
- return "Event";
+ return "ReadableEvent";
}
std::string GetName() const override {
return name;
}
- static const HandleType HANDLE_TYPE = HandleType::Event;
- HandleType GetHandleType() const override {
- return HANDLE_TYPE;
- }
-
ResetType GetResetType() const {
return reset_type;
}
+ static const HandleType HANDLE_TYPE = HandleType::ReadableEvent;
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
bool ShouldWait(Thread* thread) const override;
void Acquire(Thread* thread) override;
void WakeupAllWaitingThreads() override;
- void Signal();
+ /// Unconditionally clears the readable event's state.
void Clear();
+ /// Clears the readable event's state if and only if it
+ /// has already been signaled.
+ ///
+ /// @pre The event must be in a signaled state. If this event
+ /// is in an unsignaled state and this function is called,
+ /// then ERR_INVALID_STATE will be returned.
+ ResultCode Reset();
+
private:
- explicit Event(KernelCore& kernel);
- ~Event() override;
+ explicit ReadableEvent(KernelCore& kernel);
+
+ void Signal();
- ResetType reset_type; ///< Current ResetType
+ ResetType reset_type;
+ bool signaled;
- bool signaled; ///< Whether the event has already been signaled
std::string name; ///< Name of event (optional)
};
diff --git a/src/core/hle/kernel/resource_limit.cpp b/src/core/hle/kernel/resource_limit.cpp
index b253a680f..2f9695005 100644
--- a/src/core/hle/kernel/resource_limit.cpp
+++ b/src/core/hle/kernel/resource_limit.cpp
@@ -2,12 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-#include "common/assert.h"
-#include "common/logging/log.h"
+#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/resource_limit.h"
+#include "core/hle/result.h"
namespace Kernel {
+namespace {
+constexpr std::size_t ResourceTypeToIndex(ResourceType type) {
+ return static_cast<std::size_t>(type);
+}
+} // Anonymous namespace
ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {}
ResourceLimit::~ResourceLimit() = default;
@@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
return resource_limit;
}
-s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
- switch (resource) {
- case ResourceType::Commit:
- return current_commit;
- case ResourceType::Thread:
- return current_threads;
- case ResourceType::Event:
- return current_events;
- case ResourceType::Mutex:
- return current_mutexes;
- case ResourceType::Semaphore:
- return current_semaphores;
- case ResourceType::Timer:
- return current_timers;
- case ResourceType::SharedMemory:
- return current_shared_mems;
- case ResourceType::AddressArbiter:
- return current_address_arbiters;
- case ResourceType::CPUTime:
- return current_cpu_time;
- default:
- LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
- UNIMPLEMENTED();
- return 0;
- }
+s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
+ return values.at(ResourceTypeToIndex(resource));
+}
+
+s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
+ return limits.at(ResourceTypeToIndex(resource));
}
-u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
- switch (resource) {
- case ResourceType::Priority:
- return max_priority;
- case ResourceType::Commit:
- return max_commit;
- case ResourceType::Thread:
- return max_threads;
- case ResourceType::Event:
- return max_events;
- case ResourceType::Mutex:
- return max_mutexes;
- case ResourceType::Semaphore:
- return max_semaphores;
- case ResourceType::Timer:
- return max_timers;
- case ResourceType::SharedMemory:
- return max_shared_mems;
- case ResourceType::AddressArbiter:
- return max_address_arbiters;
- case ResourceType::CPUTime:
- return max_cpu_time;
- default:
- LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
- UNIMPLEMENTED();
- return 0;
+ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
+ const auto index = ResourceTypeToIndex(resource);
+
+ if (value < values[index]) {
+ return ERR_INVALID_STATE;
}
+
+ values[index] = value;
+ return RESULT_SUCCESS;
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/resource_limit.h b/src/core/hle/kernel/resource_limit.h
index 219e49562..59dc11c22 100644
--- a/src/core/hle/kernel/resource_limit.h
+++ b/src/core/hle/kernel/resource_limit.h
@@ -4,33 +4,31 @@
#pragma once
+#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
+union ResultCode;
+
namespace Kernel {
class KernelCore;
-enum class ResourceLimitCategory : u8 {
- APPLICATION = 0,
- SYS_APPLET = 1,
- LIB_APPLET = 2,
- OTHER = 3
-};
+enum class ResourceType : u32 {
+ PhysicalMemory,
+ Threads,
+ Events,
+ TransferMemory,
+ Sessions,
-enum class ResourceType {
- Priority = 0,
- Commit = 1,
- Thread = 2,
- Event = 3,
- Mutex = 4,
- Semaphore = 5,
- Timer = 6,
- SharedMemory = 7,
- AddressArbiter = 8,
- CPUTime = 9,
+ // Used as a count, not an actual type.
+ ResourceTypeCount
};
+constexpr bool IsValidResourceType(ResourceType type) {
+ return type < ResourceType::ResourceTypeCount;
+}
+
class ResourceLimit final : public Object {
public:
/**
@@ -55,61 +53,51 @@ public:
* @param resource Requested resource type
* @returns The current value of the resource type
*/
- s32 GetCurrentResourceValue(ResourceType resource) const;
+ s64 GetCurrentResourceValue(ResourceType resource) const;
/**
* Gets the max value for the specified resource.
* @param resource Requested resource type
* @returns The max value of the resource type
*/
- u32 GetMaxResourceValue(ResourceType resource) const;
-
- /// Name of resource limit object.
- std::string name;
-
- /// Max thread priority that a process in this category can create
- s32 max_priority = 0;
-
- /// Max memory that processes in this category can use
- s32 max_commit = 0;
+ s64 GetMaxResourceValue(ResourceType resource) const;
- ///< Max number of objects that can be collectively created by the processes in this category
- s32 max_threads = 0;
- s32 max_events = 0;
- s32 max_mutexes = 0;
- s32 max_semaphores = 0;
- s32 max_timers = 0;
- s32 max_shared_mems = 0;
- s32 max_address_arbiters = 0;
+ /**
+ * Sets the limit value for a given resource type.
+ *
+ * @param resource The resource type to apply the limit to.
+ * @param value The limit to apply to the given resource type.
+ *
+ * @return A result code indicating if setting the limit value
+ * was successful or not.
+ *
+ * @note The supplied limit value *must* be greater than or equal to
+ * the current resource value for the given resource type,
+ * otherwise ERR_INVALID_STATE will be returned.
+ */
+ ResultCode SetLimitValue(ResourceType resource, s64 value);
- /// Max CPU time that the processes in this category can utilize
- s32 max_cpu_time = 0;
+private:
+ explicit ResourceLimit(KernelCore& kernel);
+ ~ResourceLimit() override;
- // TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
- // that APPLICATION resource limits should not be affected by the objects created by service
- // modules.
+ // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create
+ // functions
+ //
// Currently we have no way of distinguishing if a Create was called by the running application,
// or by a service module. Approach this once we have separated the service modules into their
// own processes
- /// Current memory that the processes in this category are using
- s32 current_commit = 0;
+ using ResourceArray =
+ std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>;
- ///< Current number of objects among all processes in this category
- s32 current_threads = 0;
- s32 current_events = 0;
- s32 current_mutexes = 0;
- s32 current_semaphores = 0;
- s32 current_timers = 0;
- s32 current_shared_mems = 0;
- s32 current_address_arbiters = 0;
+ /// Maximum values a resource type may reach.
+ ResourceArray limits{};
+ /// Current resource limit values.
+ ResourceArray values{};
- /// Current CPU time that the processes in this category are utilizing
- s32 current_cpu_time = 0;
-
-private:
- explicit ResourceLimit(KernelCore& kernel);
- ~ResourceLimit() override;
+ /// Name of resource limit object.
+ std::string name;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index 5a5f4cef1..df4d6cf0a 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -9,6 +9,7 @@
#include "common/logging/log.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
+#include "core/core_cpu.h"
#include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
@@ -179,4 +180,69 @@ void Scheduler::SetThreadPriority(Thread* thread, u32 priority) {
ready_queue.prepare(priority);
}
+Thread* Scheduler::GetNextSuggestedThread(u32 core, u32 maximum_priority) const {
+ std::lock_guard<std::mutex> lock(scheduler_mutex);
+
+ const u32 mask = 1U << core;
+ return ready_queue.get_first_filter([mask, maximum_priority](Thread const* thread) {
+ return (thread->GetAffinityMask() & mask) != 0 && thread->GetPriority() < maximum_priority;
+ });
+}
+
+void Scheduler::YieldWithoutLoadBalancing(Thread* thread) {
+ ASSERT(thread != nullptr);
+ // Avoid yielding if the thread isn't even running.
+ ASSERT(thread->GetStatus() == ThreadStatus::Running);
+
+ // Sanity check that the priority is valid
+ ASSERT(thread->GetPriority() < THREADPRIO_COUNT);
+
+ // Yield this thread -- sleep for zero time and force reschedule to different thread
+ WaitCurrentThread_Sleep();
+ GetCurrentThread()->WakeAfterDelay(0);
+}
+
+void Scheduler::YieldWithLoadBalancing(Thread* thread) {
+ ASSERT(thread != nullptr);
+ const auto priority = thread->GetPriority();
+ const auto core = static_cast<u32>(thread->GetProcessorID());
+
+ // Avoid yielding if the thread isn't even running.
+ ASSERT(thread->GetStatus() == ThreadStatus::Running);
+
+ // Sanity check that the priority is valid
+ ASSERT(priority < THREADPRIO_COUNT);
+
+ // Sleep for zero time to be able to force reschedule to different thread
+ WaitCurrentThread_Sleep();
+ GetCurrentThread()->WakeAfterDelay(0);
+
+ Thread* suggested_thread = nullptr;
+
+ // Search through all of the cpu cores (except this one) for a suggested thread.
+ // Take the first non-nullptr one
+ for (unsigned cur_core = 0; cur_core < Core::NUM_CPU_CORES; ++cur_core) {
+ const auto res =
+ Core::System::GetInstance().CpuCore(cur_core).Scheduler().GetNextSuggestedThread(
+ core, priority);
+
+ // If scheduler provides a suggested thread
+ if (res != nullptr) {
+ // And its better than the current suggested thread (or is the first valid one)
+ if (suggested_thread == nullptr ||
+ suggested_thread->GetPriority() > res->GetPriority()) {
+ suggested_thread = res;
+ }
+ }
+ }
+
+ // If a suggested thread was found, queue that for this core
+ if (suggested_thread != nullptr)
+ suggested_thread->ChangeCore(core, suggested_thread->GetAffinityMask());
+}
+
+void Scheduler::YieldAndWaitForLoadBalancing(Thread* thread) {
+ UNIMPLEMENTED_MSG("Wait for load balancing thread yield type is not implemented!");
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index c63032b7d..97ced4dfc 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -51,6 +51,75 @@ public:
/// Sets the priority of a thread in the scheduler
void SetThreadPriority(Thread* thread, u32 priority);
+ /// Gets the next suggested thread for load balancing
+ Thread* GetNextSuggestedThread(u32 core, u32 minimum_priority) const;
+
+ /**
+ * YieldWithoutLoadBalancing -- analogous to normal yield on a system
+ * Moves the thread to the end of the ready queue for its priority, and then reschedules the
+ * system to the new head of the queue.
+ *
+ * Example (Single Core -- but can be extrapolated to multi):
+ * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC (->exec order->)
+ * Currently Running: ThreadR
+ *
+ * ThreadR calls YieldWithoutLoadBalancing
+ *
+ * ThreadR is moved to the end of ready_queue[prio=0]:
+ * ready_queue[prio=0]: ThreadA, ThreadB, ThreadC, ThreadR (->exec order->)
+ * Currently Running: Nothing
+ *
+ * System is rescheduled (ThreadA is popped off of queue):
+ * ready_queue[prio=0]: ThreadB, ThreadC, ThreadR (->exec order->)
+ * Currently Running: ThreadA
+ *
+ * If the queue is empty at time of call, no yielding occurs. This does not cross between cores
+ * or priorities at all.
+ */
+ void YieldWithoutLoadBalancing(Thread* thread);
+
+ /**
+ * YieldWithLoadBalancing -- yield but with better selection of the new running thread
+ * Moves the current thread to the end of the ready queue for its priority, then selects a
+ * 'suggested thread' (a thread on a different core that could run on this core) from the
+ * scheduler, changes its core, and reschedules the current core to that thread.
+ *
+ * Example (Dual Core -- can be extrapolated to Quad Core, this is just normal yield if it were
+ * single core):
+ * ready_queue[core=0][prio=0]: ThreadA, ThreadB (affinities not pictured as irrelevant
+ * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
+ * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
+ *
+ * ThreadQ calls YieldWithLoadBalancing
+ *
+ * ThreadQ is moved to the end of ready_queue[core=0][prio=0]:
+ * ready_queue[core=0][prio=0]: ThreadA, ThreadB
+ * ready_queue[core=1][prio=0]: ThreadC[affinity=both], ThreadD[affinity=core1only]
+ * Currently Running: ThreadQ on Core 0 || ThreadP on Core 1
+ *
+ * A list of suggested threads for each core is compiled
+ * Suggested Threads: {ThreadC on Core 1}
+ * If this were quad core (as the switch is), there could be between 0 and 3 threads in this
+ * list. If there are more than one, the thread is selected by highest prio.
+ *
+ * ThreadC is core changed to Core 0:
+ * ready_queue[core=0][prio=0]: ThreadC, ThreadA, ThreadB, ThreadQ
+ * ready_queue[core=1][prio=0]: ThreadD
+ * Currently Running: None on Core 0 || ThreadP on Core 1
+ *
+ * System is rescheduled (ThreadC is popped off of queue):
+ * ready_queue[core=0][prio=0]: ThreadA, ThreadB, ThreadQ
+ * ready_queue[core=1][prio=0]: ThreadD
+ * Currently Running: ThreadC on Core 0 || ThreadP on Core 1
+ *
+ * If no suggested threads can be found this will behave just as normal yield. If there are
+ * multiple candidates for the suggested thread on a core, the highest prio is taken.
+ */
+ void YieldWithLoadBalancing(Thread* thread);
+
+ /// Currently unknown -- asserts as unimplemented on call
+ void YieldAndWaitForLoadBalancing(Thread* thread);
+
/// Returns a list of all threads managed by the scheduler
const std::vector<SharedPtr<Thread>>& GetThreadList() const {
return thread_list;
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 5fc320403..027434f92 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -6,6 +6,7 @@
#include <utility>
#include "common/assert.h"
+#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
@@ -63,7 +64,7 @@ void ServerSession::Acquire(Thread* thread) {
}
ResultCode ServerSession::HandleDomainSyncRequest(Kernel::HLERequestContext& context) {
- auto& domain_message_header = context.GetDomainMessageHeader();
+ auto* const domain_message_header = context.GetDomainMessageHeader();
if (domain_message_header) {
// Set domain handlers in HLE context, used for domain objects (IPC interfaces) as inputs
context.SetDomainRequestHandlers(domain_request_handlers);
@@ -111,7 +112,7 @@ ResultCode ServerSession::HandleSyncRequest(SharedPtr<Thread> thread) {
ResultCode result = RESULT_SUCCESS;
// If the session has been converted to a domain, handle the domain request
- if (IsDomain() && context.GetDomainMessageHeader()) {
+ if (IsDomain() && context.HasDomainMessageHeader()) {
result = HandleDomainSyncRequest(context);
// If there is no domain header, the regular session handler is used
} else if (hle_handler != nullptr) {
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index e068db2bf..e0e9d64c8 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -8,7 +8,6 @@
#include <string>
#include <vector>
-#include "common/common_types.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/wait_object.h"
#include "core/hle/result.h"
diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp
index a016a86b6..22d0c1dd5 100644
--- a/src/core/hle/kernel/shared_memory.cpp
+++ b/src/core/hle/kernel/shared_memory.cpp
@@ -17,13 +17,13 @@ namespace Kernel {
SharedMemory::SharedMemory(KernelCore& kernel) : Object{kernel} {}
SharedMemory::~SharedMemory() = default;
-SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Process> owner_process,
- u64 size, MemoryPermission permissions,
+SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, Process* owner_process, u64 size,
+ MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address,
MemoryRegion region, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
- shared_memory->owner_process = std::move(owner_process);
+ shared_memory->owner_process = owner_process;
shared_memory->name = std::move(name);
shared_memory->size = size;
shared_memory->permissions = permissions;
@@ -39,15 +39,15 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
shared_memory->backing_block.get());
}
} else {
- auto& vm_manager = shared_memory->owner_process->VMManager();
+ const auto& vm_manager = shared_memory->owner_process->VMManager();
// The memory is already available and mapped in the owner process.
- auto vma = vm_manager.FindVMA(address);
- ASSERT_MSG(vma != vm_manager.vma_map.end(), "Invalid memory address");
+ const auto vma = vm_manager.FindVMA(address);
+ ASSERT_MSG(vm_manager.IsValidHandle(vma), "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;
+ const auto vma_offset = address - vma->first;
ASSERT_MSG(vma_offset + size <= vma->second.size,
"Shared memory exceeds bounds of mapped block");
@@ -61,7 +61,7 @@ SharedPtr<SharedMemory> SharedMemory::Create(KernelCore& kernel, SharedPtr<Proce
}
SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
- KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, u32 offset, u32 size,
+ KernelCore& kernel, std::shared_ptr<std::vector<u8>> heap_block, std::size_t offset, u64 size,
MemoryPermission permissions, MemoryPermission other_permissions, std::string name) {
SharedPtr<SharedMemory> shared_memory(new SharedMemory(kernel));
@@ -78,10 +78,10 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet(
return shared_memory;
}
-ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermission permissions,
+ResultCode SharedMemory::Map(Process& target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions) {
const MemoryPermission own_other_permissions =
- target_process == owner_process ? this->permissions : this->other_permissions;
+ &target_process == owner_process ? this->permissions : this->other_permissions;
// Automatically allocated memory blocks can only be mapped with other_permissions = DontCare
if (base_address == 0 && other_permissions != MemoryPermission::DontCare) {
@@ -106,7 +106,7 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
VAddr target_address = address;
// Map the memory block into the target process
- auto result = target_process->VMManager().MapMemoryBlock(
+ auto result = target_process.VMManager().MapMemoryBlock(
target_address, backing_block, backing_block_offset, size, MemoryState::Shared);
if (result.Failed()) {
LOG_ERROR(
@@ -116,14 +116,14 @@ ResultCode SharedMemory::Map(Process* target_process, VAddr address, MemoryPermi
return result.Code();
}
- return target_process->VMManager().ReprotectRange(target_address, size,
- ConvertPermissions(permissions));
+ return target_process.VMManager().ReprotectRange(target_address, size,
+ ConvertPermissions(permissions));
}
-ResultCode SharedMemory::Unmap(Process* target_process, VAddr address) {
+ResultCode SharedMemory::Unmap(Process& target_process, VAddr address) {
// TODO(Subv): Verify what happens if the application tries to unmap an address that is not
// mapped to a SharedMemory.
- return target_process->VMManager().UnmapRange(address, size);
+ return target_process.VMManager().UnmapRange(address, size);
}
VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
@@ -132,7 +132,11 @@ VMAPermission SharedMemory::ConvertPermissions(MemoryPermission permission) {
return static_cast<VMAPermission>(masked_permissions);
}
-u8* SharedMemory::GetPointer(u32 offset) {
+u8* SharedMemory::GetPointer(std::size_t offset) {
+ return backing_block->data() + backing_block_offset + offset;
+}
+
+const u8* SharedMemory::GetPointer(std::size_t offset) const {
return backing_block->data() + backing_block_offset + offset;
}
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 2c06bb7ce..dab2a6bea 100644
--- a/src/core/hle/kernel/shared_memory.h
+++ b/src/core/hle/kernel/shared_memory.h
@@ -45,8 +45,8 @@ public:
* linear heap.
* @param name Optional object name, used for debugging purposes.
*/
- static SharedPtr<SharedMemory> Create(KernelCore& kernel, SharedPtr<Process> owner_process,
- u64 size, MemoryPermission permissions,
+ static SharedPtr<SharedMemory> Create(KernelCore& kernel, Process* owner_process, u64 size,
+ MemoryPermission permissions,
MemoryPermission other_permissions, VAddr address = 0,
MemoryRegion region = MemoryRegion::BASE,
std::string name = "Unknown");
@@ -64,7 +64,7 @@ public:
*/
static SharedPtr<SharedMemory> CreateForApplet(KernelCore& kernel,
std::shared_ptr<std::vector<u8>> heap_block,
- u32 offset, u32 size,
+ std::size_t offset, u64 size,
MemoryPermission permissions,
MemoryPermission other_permissions,
std::string name = "Unknown Applet");
@@ -81,6 +81,11 @@ public:
return HANDLE_TYPE;
}
+ /// Gets the size of the underlying memory block in bytes.
+ u64 GetSize() const {
+ return size;
+ }
+
/**
* Converts the specified MemoryPermission into the equivalent VMAPermission.
* @param permission The MemoryPermission to convert.
@@ -94,44 +99,51 @@ public:
* @param permissions Memory block map permissions (specified by SVC field)
* @param other_permissions Memory block map other permissions (specified by SVC field)
*/
- ResultCode Map(Process* target_process, VAddr address, MemoryPermission permissions,
+ ResultCode Map(Process& target_process, VAddr address, MemoryPermission permissions,
MemoryPermission other_permissions);
/**
* Unmaps a shared memory block from the specified address in system memory
- * @param target_process Process from which to umap the memory block.
+ * @param target_process Process from which to unmap the memory block.
* @param address Address in system memory where the shared memory block is mapped
* @return Result code of the unmap operation
*/
- ResultCode Unmap(Process* target_process, VAddr address);
+ ResultCode Unmap(Process& target_process, VAddr address);
/**
* Gets a pointer to the shared memory block
* @param offset Offset from the start of the shared memory block to get pointer
- * @return Pointer to the shared memory block from the specified offset
+ * @return A pointer to the shared memory block from the specified offset
*/
- u8* GetPointer(u32 offset = 0);
+ u8* GetPointer(std::size_t offset = 0);
+
+ /**
+ * Gets a constant pointer to the shared memory block
+ * @param offset Offset from the start of the shared memory block to get pointer
+ * @return A constant pointer to the shared memory block from the specified offset
+ */
+ const u8* GetPointer(std::size_t offset = 0) const;
+
+private:
+ explicit SharedMemory(KernelCore& kernel);
+ ~SharedMemory() override;
- /// Process that created this shared memory block.
- SharedPtr<Process> owner_process;
- /// Address of shared memory block in the owner process if specified.
- VAddr base_address;
/// Backing memory for this shared memory block.
std::shared_ptr<std::vector<u8>> backing_block;
/// Offset into the backing block for this shared memory.
- std::size_t backing_block_offset;
+ std::size_t backing_block_offset = 0;
/// Size of the memory block. Page-aligned.
- u64 size;
+ u64 size = 0;
/// Permission restrictions applied to the process which created the block.
- MemoryPermission permissions;
+ MemoryPermission permissions{};
/// Permission restrictions applied to other processes mapping the block.
- MemoryPermission other_permissions;
+ MemoryPermission other_permissions{};
+ /// Process that created this shared memory block.
+ Process* owner_process;
+ /// Address of shared memory block in the owner process if specified.
+ VAddr base_address = 0;
/// Name of shared memory object.
std::string name;
-
-private:
- explicit SharedMemory(KernelCore& kernel);
- ~SharedMemory() override;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 4e490e2b5..6588bd3b8 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -20,20 +20,22 @@
#include "core/hle/kernel/address_arbiter.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/event.h"
#include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/mutex.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/resource_limit.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/kernel/shared_memory.h"
#include "core/hle/kernel/svc.h"
#include "core/hle/kernel/svc_wrap.h"
#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
+#include "core/memory.h"
namespace Kernel {
namespace {
@@ -62,71 +64,248 @@ bool IsInsideNewMapRegion(const VMManager& vm, VAddr address, u64 size) {
vm.GetNewMapRegionEndAddress());
}
+// 8 GiB
+constexpr u64 MAIN_MEMORY_SIZE = 0x200000000;
+
// Helper function that performs the common sanity checks for svcMapMemory
// and svcUnmapMemory. This is doable, as both functions perform their sanitizing
// in the same order.
ResultCode MapUnmapMemorySanityChecks(const VMManager& vm_manager, VAddr dst_addr, VAddr src_addr,
u64 size) {
- if (!Common::Is4KBAligned(dst_addr) || !Common::Is4KBAligned(src_addr)) {
+ if (!Common::Is4KBAligned(dst_addr)) {
+ LOG_ERROR(Kernel_SVC, "Destination address is not aligned to 4KB, 0x{:016X}", dst_addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (!Common::Is4KBAligned(src_addr)) {
+ LOG_ERROR(Kernel_SVC, "Source address is not aligned to 4KB, 0x{:016X}", src_addr);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ dst_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsValidAddressRange(src_addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Source is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideAddressSpace(vm_manager, src_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}",
+ src_addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
if (!IsInsideNewMapRegion(vm_manager, dst_addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination is not within the new map region, addr=0x{:016X}, size=0x{:016X}",
+ dst_addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
const VAddr dst_end_address = dst_addr + size;
if (dst_end_address > vm_manager.GetHeapRegionBaseAddress() &&
vm_manager.GetHeapRegionEndAddress() > dst_addr) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination does not fit within the heap region, addr=0x{:016X}, "
+ "size=0x{:016X}, end_addr=0x{:016X}",
+ dst_addr, size, dst_end_address);
return ERR_INVALID_MEMORY_RANGE;
}
if (dst_end_address > vm_manager.GetMapRegionBaseAddress() &&
vm_manager.GetMapRegionEndAddress() > dst_addr) {
+ LOG_ERROR(Kernel_SVC,
+ "Destination does not fit within the map region, addr=0x{:016X}, "
+ "size=0x{:016X}, end_addr=0x{:016X}",
+ dst_addr, size, dst_end_address);
return ERR_INVALID_MEMORY_RANGE;
}
return RESULT_SUCCESS;
}
+
+enum class ResourceLimitValueType {
+ CurrentValue,
+ LimitValue,
+};
+
+ResultVal<s64> RetrieveResourceLimitValue(Handle resource_limit, u32 resource_type,
+ ResourceLimitValueType value_type) {
+ const auto type = static_cast<ResourceType>(resource_type);
+ if (!IsValidResourceType(type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ const auto& kernel = Core::System::GetInstance().Kernel();
+ const auto* const current_process = kernel.CurrentProcess();
+ ASSERT(current_process != nullptr);
+
+ const auto resource_limit_object =
+ current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
+ if (!resource_limit_object) {
+ LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
+ resource_limit);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (value_type == ResourceLimitValueType::CurrentValue) {
+ return MakeResult(resource_limit_object->GetCurrentResourceValue(type));
+ }
+
+ return MakeResult(resource_limit_object->GetMaxResourceValue(type));
+}
} // Anonymous namespace
/// Set the process heap to a given Size. It can both extend and shrink the heap.
static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) {
LOG_TRACE(Kernel_SVC, "called, heap_size=0x{:X}", heap_size);
- // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 4GB.
- if ((heap_size & 0xFFFFFFFE001FFFFF) != 0) {
+ // Size must be a multiple of 0x200000 (2MB) and be equal to or less than 8GB.
+ if ((heap_size % 0x200000) != 0) {
+ LOG_ERROR(Kernel_SVC, "The heap size is not a multiple of 2MB, heap_size=0x{:016X}",
+ heap_size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (heap_size >= 0x200000000) {
+ LOG_ERROR(Kernel_SVC, "The heap size is not less than 8GB, heap_size=0x{:016X}", heap_size);
return ERR_INVALID_SIZE;
}
- auto& process = *Core::CurrentProcess();
- const VAddr heap_base = process.VMManager().GetHeapRegionBaseAddress();
- CASCADE_RESULT(*heap_addr,
- process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite));
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
+ const VAddr heap_base = vm_manager.GetHeapRegionBaseAddress();
+ const auto alloc_result =
+ vm_manager.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite);
+
+ if (alloc_result.Failed()) {
+ return alloc_result.Code();
+ }
+
+ *heap_addr = *alloc_result;
return RESULT_SUCCESS;
}
-static ResultCode SetMemoryAttribute(VAddr addr, u64 size, u32 state0, u32 state1) {
- LOG_WARNING(Kernel_SVC,
- "(STUBBED) called, addr=0x{:X}, size=0x{:X}, state0=0x{:X}, state1=0x{:X}", addr,
- size, state0, state1);
- return RESULT_SUCCESS;
+static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
+ LOG_TRACE(Kernel_SVC, "called, addr=0x{:X}, size=0x{:X}, prot=0x{:X}", addr, size, prot);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ addr, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto permission = static_cast<MemoryPermission>(prot);
+ if (permission != MemoryPermission::None && permission != MemoryPermission::Read &&
+ permission != MemoryPermission::ReadWrite) {
+ LOG_ERROR(Kernel_SVC, "Invalid memory permission specified, Got memory permission=0x{:08X}",
+ static_cast<u32>(permission));
+ return ERR_INVALID_MEMORY_PERMISSIONS;
+ }
+
+ auto* const current_process = Core::CurrentProcess();
+ auto& vm_manager = current_process->VMManager();
+
+ if (!IsInsideAddressSpace(vm_manager, addr, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Source is not within the address space, addr=0x{:016X}, size=0x{:016X}", addr,
+ size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
+ if (!vm_manager.IsValidHandle(iter)) {
+ LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ LOG_WARNING(Kernel_SVC, "Uniformity check on protected memory is not implemented.");
+ // TODO: Performs a uniformity check to make sure only protected memory is changed (it doesn't
+ // make sense to allow changing permissions on kernel memory itself, etc).
+
+ const auto converted_permissions = SharedMemory::ConvertPermissions(permission);
+
+ return vm_manager.ReprotectRange(addr, size, converted_permissions);
+}
+
+static ResultCode SetMemoryAttribute(VAddr address, u64 size, u32 mask, u32 attribute) {
+ LOG_DEBUG(Kernel_SVC,
+ "called, address=0x{:016X}, size=0x{:X}, mask=0x{:08X}, attribute=0x{:08X}", address,
+ size, mask, attribute);
+
+ if (!Common::Is4KBAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Address not page aligned (0x{:016X})", address);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0 || !Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Invalid size (0x{:X}). Size must be non-zero and page aligned.",
+ size);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!IsValidAddressRange(address, size)) {
+ LOG_ERROR(Kernel_SVC, "Address range overflowed (Address: 0x{:016X}, Size: 0x{:016X})",
+ address, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto mem_attribute = static_cast<MemoryAttribute>(attribute);
+ const auto mem_mask = static_cast<MemoryAttribute>(mask);
+ const auto attribute_with_mask = mem_attribute | mem_mask;
+
+ if (attribute_with_mask != mem_mask) {
+ LOG_ERROR(Kernel_SVC,
+ "Memory attribute doesn't match the given mask (Attribute: 0x{:X}, Mask: {:X}",
+ attribute, mask);
+ return ERR_INVALID_COMBINATION;
+ }
+
+ if ((attribute_with_mask | MemoryAttribute::Uncached) != MemoryAttribute::Uncached) {
+ LOG_ERROR(Kernel_SVC, "Specified attribute isn't equal to MemoryAttributeUncached (8).");
+ return ERR_INVALID_COMBINATION;
+ }
+
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
+ if (!IsInsideAddressSpace(vm_manager, address, size)) {
+ LOG_ERROR(Kernel_SVC,
+ "Given address (0x{:016X}) is outside the bounds of the address space.", address);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ return vm_manager.SetMemoryAttribute(address, size, mem_mask, mem_attribute);
}
/// Maps a memory range into a different range.
@@ -134,15 +313,14 @@ static ResultCode MapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto* const current_process = Core::CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
-
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
- if (result != RESULT_SUCCESS) {
+
+ if (result.IsError()) {
return result;
}
- return current_process->MirrorMemory(dst_addr, src_addr, size);
+ return vm_manager.MirrorMemory(dst_addr, src_addr, size, MemoryState::Stack);
}
/// Unmaps a region that was previously mapped with svcMapMemory
@@ -150,20 +328,22 @@ static ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size) {
LOG_TRACE(Kernel_SVC, "called, dst_addr=0x{:X}, src_addr=0x{:X}, size=0x{:X}", dst_addr,
src_addr, size);
- auto* const current_process = Core::CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
-
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto result = MapUnmapMemorySanityChecks(vm_manager, dst_addr, src_addr, size);
- if (result != RESULT_SUCCESS) {
+
+ if (result.IsError()) {
return result;
}
- return current_process->UnmapMemory(dst_addr, src_addr, size);
+ return vm_manager.UnmapRange(dst_addr, size);
}
/// Connect to an OS service given the port name, returns the handle to the port to out
static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address) {
if (!Memory::IsValidVirtualAddress(port_name_address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Port Name Address is not a valid virtual address, port_name_address=0x{:016X}",
+ port_name_address);
return ERR_NOT_FOUND;
}
@@ -171,7 +351,9 @@ static ResultCode ConnectToNamedPort(Handle* out_handle, VAddr port_name_address
// 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 ERR_PORT_NAME_TOO_LONG;
+ LOG_ERROR(Kernel_SVC, "Port name is too long, expected {} but got {}", PortNameMaxLength,
+ port_name.size());
+ return ERR_OUT_OF_RANGE;
}
LOG_TRACE(Kernel_SVC, "called port_name={}", port_name);
@@ -213,12 +395,13 @@ static ResultCode SendSyncRequest(Handle handle) {
}
/// Get the ID for the specified thread.
-static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
+static ResultCode GetThreadId(u64* thread_id, Handle thread_handle) {
LOG_TRACE(Kernel_SVC, "called thread=0x{:08X}", thread_handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", thread_handle);
return ERR_INVALID_HANDLE;
}
@@ -226,18 +409,33 @@ static ResultCode GetThreadId(u32* thread_id, Handle thread_handle) {
return RESULT_SUCCESS;
}
-/// Get the ID of the specified process
-static ResultCode GetProcessId(u32* process_id, Handle process_handle) {
- LOG_TRACE(Kernel_SVC, "called process=0x{:08X}", process_handle);
+/// Gets the ID of the specified process or a specified thread's owning process.
+static ResultCode GetProcessId(u64* process_id, Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called handle=0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- const SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
- if (!process) {
- return ERR_INVALID_HANDLE;
+ const SharedPtr<Process> process = handle_table.Get<Process>(handle);
+ if (process) {
+ *process_id = process->GetProcessID();
+ return RESULT_SUCCESS;
}
- *process_id = process->GetProcessID();
- return RESULT_SUCCESS;
+ const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
+ if (thread) {
+ const Process* const owner_process = thread->GetOwnerProcess();
+ if (!owner_process) {
+ LOG_ERROR(Kernel_SVC, "Non-existent owning process encountered.");
+ return ERR_INVALID_HANDLE;
+ }
+
+ *process_id = owner_process->GetProcessID();
+ return RESULT_SUCCESS;
+ }
+
+ // NOTE: This should also handle debug objects before returning.
+
+ LOG_ERROR(Kernel_SVC, "Handle does not exist, handle=0x{:08X}", handle);
+ return ERR_INVALID_HANDLE;
}
/// Default thread wakeup callback for WaitSynchronization
@@ -262,13 +460,20 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
LOG_TRACE(Kernel_SVC, "called handles_address=0x{:X}, handle_count={}, nano_seconds={}",
handles_address, handle_count, nano_seconds);
- if (!Memory::IsValidVirtualAddress(handles_address))
+ if (!Memory::IsValidVirtualAddress(handles_address)) {
+ LOG_ERROR(Kernel_SVC,
+ "Handle address is not a valid virtual address, handle_address=0x{:016X}",
+ handles_address);
return ERR_INVALID_POINTER;
+ }
static constexpr u64 MaxHandles = 0x40;
- if (handle_count > MaxHandles)
- return ResultCode(ErrorModule::Kernel, ErrCodes::TooLarge);
+ if (handle_count > MaxHandles) {
+ LOG_ERROR(Kernel_SVC, "Handle count specified is too large, expected {} but got {}",
+ MaxHandles, handle_count);
+ return ERR_OUT_OF_RANGE;
+ }
auto* const thread = GetCurrentThread();
@@ -281,6 +486,7 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
const auto object = handle_table.Get<WaitObject>(handle);
if (object == nullptr) {
+ LOG_ERROR(Kernel_SVC, "Object is a nullptr");
return ERR_INVALID_HANDLE;
}
@@ -304,11 +510,13 @@ static ResultCode WaitSynchronization(Handle* index, VAddr handles_address, u64
// If a timeout value of 0 was provided, just return the Timeout error code instead of
// suspending the thread.
- if (nano_seconds == 0)
+ if (nano_seconds == 0) {
return RESULT_TIMEOUT;
+ }
- for (auto& object : objects)
+ for (auto& object : objects) {
object->AddWaitingThread(thread);
+ }
thread->SetWaitObjects(std::move(objects));
thread->SetStatus(ThreadStatus::WaitSynchAny);
@@ -329,12 +537,13 @@ static ResultCode CancelSynchronization(Handle thread_handle) {
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
ASSERT(thread->GetStatus() == ThreadStatus::WaitSynchAny);
- thread->SetWaitSynchronizationResult(
- ResultCode(ErrorModule::Kernel, ErrCodes::SynchronizationCanceled));
+ thread->SetWaitSynchronizationResult(ERR_SYNCHRONIZATION_CANCELED);
thread->ResumeFromWait();
return RESULT_SUCCESS;
}
@@ -348,10 +557,13 @@ static ResultCode ArbitrateLock(Handle holding_thread_handle, VAddr mutex_addr,
holding_thread_handle, mutex_addr, requesting_thread_handle);
if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
+ mutex_addr);
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
return ERR_INVALID_ADDRESS;
}
@@ -365,10 +577,13 @@ static ResultCode ArbitrateUnlock(VAddr mutex_addr) {
LOG_TRACE(Kernel_SVC, "called mutex_addr=0x{:X}", mutex_addr);
if (Memory::IsKernelVirtualAddress(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is a kernel virtual address, mutex_addr={:016X}",
+ mutex_addr);
return ERR_INVALID_ADDRESS_STATE;
}
if (!Common::IsWordAligned(mutex_addr)) {
+ LOG_ERROR(Kernel_SVC, "Mutex Address is not word aligned, mutex_addr={:016X}", mutex_addr);
return ERR_INVALID_ADDRESS;
}
@@ -395,16 +610,42 @@ struct BreakReason {
/// Break program execution
static void Break(u32 reason, u64 info1, u64 info2) {
BreakReason break_reason{reason};
+ bool has_dumped_buffer{};
+
+ const auto handle_debug_buffer = [&](VAddr addr, u64 sz) {
+ if (sz == 0 || addr == 0 || has_dumped_buffer) {
+ return;
+ }
+ // This typically is an error code so we're going to assume this is the case
+ if (sz == sizeof(u32)) {
+ LOG_CRITICAL(Debug_Emulated, "debug_buffer_err_code={:X}", Memory::Read32(addr));
+ } else {
+ // We don't know what's in here so we'll hexdump it
+ std::vector<u8> debug_buffer(sz);
+ Memory::ReadBlock(addr, debug_buffer.data(), sz);
+ std::string hexdump;
+ for (std::size_t i = 0; i < debug_buffer.size(); i++) {
+ hexdump += fmt::format("{:02X} ", debug_buffer[i]);
+ if (i != 0 && i % 16 == 0) {
+ hexdump += '\n';
+ }
+ }
+ LOG_CRITICAL(Debug_Emulated, "debug_buffer=\n{}", hexdump);
+ }
+ has_dumped_buffer = true;
+ };
switch (break_reason.break_type) {
case BreakType::Panic:
LOG_CRITICAL(Debug_Emulated, "Signalling debugger, PANIC! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
+ handle_debug_buffer(info1, info2);
break;
case BreakType::AssertionFailed:
LOG_CRITICAL(Debug_Emulated,
"Signalling debugger, Assertion failed! info1=0x{:016X}, info2=0x{:016X}",
info1, info2);
+ handle_debug_buffer(info1, info2);
break;
case BreakType::PreNROLoad:
LOG_WARNING(
@@ -433,6 +674,7 @@ static void Break(u32 reason, u64 info1, u64 info2) {
Debug_Emulated,
"Signalling debugger, Unknown break reason {}, info1=0x{:016X}, info2=0x{:016X}",
static_cast<u32>(break_reason.break_type.Value()), info1, info2);
+ handle_debug_buffer(info1, info2);
break;
}
@@ -441,6 +683,10 @@ static void Break(u32 reason, u64 info1, u64 info2) {
Debug_Emulated,
"Emulated program broke execution! reason=0x{:016X}, info1=0x{:016X}, info2=0x{:016X}",
reason, info1, info2);
+ handle_debug_buffer(info1, info2);
+ Core::System::GetInstance()
+ .ArmInterface(static_cast<std::size_t>(GetCurrentThread()->GetProcessorID()))
+ .LogBacktrace();
ASSERT(false);
Core::CurrentProcess()->PrepareForTermination();
@@ -469,8 +715,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
enum class GetInfoType : u64 {
// 1.0.0+
- AllowedCpuIdBitmask = 0,
- AllowedThreadPrioBitmask = 1,
+ AllowedCPUCoreMask = 0,
+ AllowedThreadPriorityMask = 1,
MapRegionBaseAddr = 2,
MapRegionSize = 3,
HeapRegionBaseAddr = 4,
@@ -478,7 +724,7 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
TotalMemoryUsage = 6,
TotalHeapUsage = 7,
IsCurrentProcessBeingDebugged = 8,
- ResourceHandleLimit = 9,
+ RegisterResourceLimit = 9,
IdleTickCount = 10,
RandomEntropy = 11,
PerformanceCounter = 0xF0000002,
@@ -498,81 +744,176 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
ThreadTickCount = 0xF0000002,
};
- const auto* current_process = Core::CurrentProcess();
- const auto& vm_manager = current_process->VMManager();
+ const auto info_id_type = static_cast<GetInfoType>(info_id);
- switch (static_cast<GetInfoType>(info_id)) {
- case GetInfoType::AllowedCpuIdBitmask:
- *result = current_process->GetAllowedProcessorMask();
- break;
- case GetInfoType::AllowedThreadPrioBitmask:
- *result = current_process->GetAllowedThreadPriorityMask();
- break;
+ switch (info_id_type) {
+ case GetInfoType::AllowedCPUCoreMask:
+ case GetInfoType::AllowedThreadPriorityMask:
case GetInfoType::MapRegionBaseAddr:
- *result = vm_manager.GetMapRegionBaseAddress();
- break;
case GetInfoType::MapRegionSize:
- *result = vm_manager.GetMapRegionSize();
- break;
case GetInfoType::HeapRegionBaseAddr:
- *result = vm_manager.GetHeapRegionBaseAddress();
- break;
case GetInfoType::HeapRegionSize:
- *result = vm_manager.GetHeapRegionSize();
- break;
- case GetInfoType::TotalMemoryUsage:
- *result = vm_manager.GetTotalMemoryUsage();
- break;
- case GetInfoType::TotalHeapUsage:
- *result = vm_manager.GetTotalHeapUsage();
- break;
- case GetInfoType::IsCurrentProcessBeingDebugged:
- *result = 0;
- break;
- case GetInfoType::RandomEntropy:
- *result = 0;
- break;
case GetInfoType::ASLRRegionBaseAddr:
- *result = vm_manager.GetASLRRegionBaseAddress();
- break;
case GetInfoType::ASLRRegionSize:
- *result = vm_manager.GetASLRRegionSize();
- break;
case GetInfoType::NewMapRegionBaseAddr:
- *result = vm_manager.GetNewMapRegionBaseAddress();
- break;
case GetInfoType::NewMapRegionSize:
- *result = vm_manager.GetNewMapRegionSize();
- break;
+ case GetInfoType::TotalMemoryUsage:
+ case GetInfoType::TotalHeapUsage:
case GetInfoType::IsVirtualAddressMemoryEnabled:
- *result = current_process->IsVirtualMemoryEnabled();
- break;
+ case GetInfoType::PersonalMmHeapUsage:
case GetInfoType::TitleId:
- *result = current_process->GetTitleID();
- break;
+ case GetInfoType::UserExceptionContextAddr: {
+ if (info_sub_id != 0) {
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ const auto& current_process_handle_table = Core::CurrentProcess()->GetHandleTable();
+ const auto process = current_process_handle_table.Get<Process>(static_cast<Handle>(handle));
+ if (!process) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ switch (info_id_type) {
+ case GetInfoType::AllowedCPUCoreMask:
+ *result = process->GetCoreMask();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::AllowedThreadPriorityMask:
+ *result = process->GetPriorityMask();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::MapRegionBaseAddr:
+ *result = process->VMManager().GetMapRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::MapRegionSize:
+ *result = process->VMManager().GetMapRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::HeapRegionBaseAddr:
+ *result = process->VMManager().GetHeapRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::HeapRegionSize:
+ *result = process->VMManager().GetHeapRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::ASLRRegionBaseAddr:
+ *result = process->VMManager().GetASLRRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::ASLRRegionSize:
+ *result = process->VMManager().GetASLRRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::NewMapRegionBaseAddr:
+ *result = process->VMManager().GetNewMapRegionBaseAddress();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::NewMapRegionSize:
+ *result = process->VMManager().GetNewMapRegionSize();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TotalMemoryUsage:
+ *result = process->VMManager().GetTotalMemoryUsage();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TotalHeapUsage:
+ *result = process->VMManager().GetTotalHeapUsage();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::IsVirtualAddressMemoryEnabled:
+ *result = process->IsVirtualMemoryEnabled();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::TitleId:
+ *result = process->GetTitleID();
+ return RESULT_SUCCESS;
+
+ case GetInfoType::UserExceptionContextAddr:
+ LOG_WARNING(Kernel_SVC,
+ "(STUBBED) Attempted to query user exception context address, returned 0");
+ *result = 0;
+ return RESULT_SUCCESS;
+
+ default:
+ break;
+ }
+
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ case GetInfoType::IsCurrentProcessBeingDebugged:
+ *result = 0;
+ return RESULT_SUCCESS;
+
+ case GetInfoType::RegisterResourceLimit: {
+ if (handle != 0) {
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (info_sub_id != 0) {
+ return ERR_INVALID_COMBINATION;
+ }
+
+ Process* const current_process = Core::CurrentProcess();
+ HandleTable& handle_table = current_process->GetHandleTable();
+ const auto resource_limit = current_process->GetResourceLimit();
+ if (!resource_limit) {
+ *result = KernelHandle::InvalidHandle;
+ // Yes, the kernel considers this a successful operation.
+ return RESULT_SUCCESS;
+ }
+
+ const auto table_result = handle_table.Create(resource_limit);
+ if (table_result.Failed()) {
+ return table_result.Code();
+ }
+
+ *result = *table_result;
+ return RESULT_SUCCESS;
+ }
+
+ case GetInfoType::RandomEntropy:
+ if (handle != 0) {
+ LOG_ERROR(Kernel_SVC, "Process Handle is non zero, expected 0 result but got {:016X}",
+ handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (info_sub_id >= Process::RANDOM_ENTROPY_SIZE) {
+ LOG_ERROR(Kernel_SVC, "Entropy size is out of range, expected {} but got {}",
+ Process::RANDOM_ENTROPY_SIZE, info_sub_id);
+ return ERR_INVALID_COMBINATION;
+ }
+
+ *result = Core::CurrentProcess()->GetRandomEntropy(info_sub_id);
+ return RESULT_SUCCESS;
+
case GetInfoType::PrivilegedProcessId:
LOG_WARNING(Kernel_SVC,
"(STUBBED) Attempted to query privileged process id bounds, returned 0");
*result = 0;
- break;
- case GetInfoType::UserExceptionContextAddr:
- LOG_WARNING(Kernel_SVC,
- "(STUBBED) Attempted to query user exception context address, returned 0");
- *result = 0;
- break;
+ return RESULT_SUCCESS;
+
case GetInfoType::ThreadTickCount: {
constexpr u64 num_cpus = 4;
if (info_sub_id != 0xFFFFFFFFFFFFFFFF && info_sub_id >= num_cpus) {
- return ERR_INVALID_COMBINATION_KERNEL;
+ LOG_ERROR(Kernel_SVC, "Core count is out of range, expected {} but got {}", num_cpus,
+ info_sub_id);
+ return ERR_INVALID_COMBINATION;
}
const auto thread =
- current_process->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
+ Core::CurrentProcess()->GetHandleTable().Get<Thread>(static_cast<Handle>(handle));
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}",
+ static_cast<Handle>(handle));
return ERR_INVALID_HANDLE;
}
- auto& system = Core::System::GetInstance();
+ const auto& system = Core::System::GetInstance();
const auto& scheduler = system.CurrentScheduler();
const auto* const current_thread = scheduler.GetCurrentThread();
const bool same_thread = current_thread == thread;
@@ -588,18 +929,45 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
*result = out_ticks;
- break;
+ return RESULT_SUCCESS;
}
+
default:
- UNIMPLEMENTED();
+ LOG_WARNING(Kernel_SVC, "(STUBBED) Unimplemented svcGetInfo id=0x{:016X}", info_id);
+ return ERR_INVALID_ENUM_VALUE;
}
-
- return RESULT_SUCCESS;
}
/// Sets the thread activity
-static ResultCode SetThreadActivity(Handle handle, u32 unknown) {
- LOG_WARNING(Kernel_SVC, "(STUBBED) called, handle=0x{:08X}, unknown=0x{:08X}", handle, unknown);
+static ResultCode SetThreadActivity(Handle handle, u32 activity) {
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, activity=0x{:08X}", handle, activity);
+ if (activity > static_cast<u32>(ThreadActivity::Paused)) {
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ const auto* current_process = Core::CurrentProcess();
+ const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
+ if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (thread->GetOwnerProcess() != current_process) {
+ LOG_ERROR(Kernel_SVC,
+ "The current process does not own the current thread, thread_handle={:08X} "
+ "thread_pid={}, "
+ "current_process_pid={}",
+ handle, thread->GetOwnerProcess()->GetProcessID(),
+ current_process->GetProcessID());
+ return ERR_INVALID_HANDLE;
+ }
+
+ if (thread == GetCurrentThread()) {
+ LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
+ return ERR_BUSY;
+ }
+
+ thread->SetActivity(static_cast<ThreadActivity>(activity));
return RESULT_SUCCESS;
}
@@ -610,15 +978,23 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
const auto* current_process = Core::CurrentProcess();
const SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
if (thread->GetOwnerProcess() != current_process) {
+ LOG_ERROR(Kernel_SVC,
+ "The current process does not own the current thread, thread_handle={:08X} "
+ "thread_pid={}, "
+ "current_process_pid={}",
+ handle, thread->GetOwnerProcess()->GetProcessID(),
+ current_process->GetProcessID());
return ERR_INVALID_HANDLE;
}
if (thread == GetCurrentThread()) {
- return ERR_ALREADY_REGISTERED;
+ LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
+ return ERR_BUSY;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
@@ -638,9 +1014,12 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
/// Gets the priority for the specified thread
static ResultCode GetThreadPriority(u32* priority, Handle handle) {
+ LOG_TRACE(Kernel_SVC, "called");
+
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
@@ -650,21 +1029,21 @@ static ResultCode GetThreadPriority(u32* priority, Handle handle) {
/// Sets the priority for the specified thread
static ResultCode SetThreadPriority(Handle handle, u32 priority) {
+ LOG_TRACE(Kernel_SVC, "called");
+
if (priority > THREADPRIO_LOWEST) {
+ LOG_ERROR(
+ Kernel_SVC,
+ "An invalid priority was specified, expected {} but got {} for thread_handle={:08X}",
+ THREADPRIO_LOWEST, priority, handle);
return ERR_INVALID_THREAD_PRIORITY;
}
const auto* const current_process = Core::CurrentProcess();
- // Note: The kernel uses the current process's resource limit instead of
- // the one from the thread owner's resource limit.
- const ResourceLimit& resource_limit = current_process->GetResourceLimit();
- if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_INVALID_THREAD_PRIORITY;
- }
-
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, handle=0x{:08X}", handle);
return ERR_INVALID_HANDLE;
}
@@ -687,36 +1066,50 @@ static ResultCode MapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 s
shared_memory_handle, addr, size, permissions);
if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
const auto permissions_type = static_cast<MemoryPermission>(permissions);
if (permissions_type != MemoryPermission::Read &&
permissions_type != MemoryPermission::ReadWrite) {
- LOG_ERROR(Kernel_SVC, "Invalid permissions=0x{:08X}", permissions);
+ LOG_ERROR(Kernel_SVC, "Expected Read or ReadWrite permission but got permissions=0x{:08X}",
+ permissions);
return ERR_INVALID_MEMORY_PERMISSIONS;
}
auto* const current_process = Core::CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
+ LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
+ shared_memory_handle);
return ERR_INVALID_HANDLE;
}
const auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}",
+ addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
- return shared_memory->Map(current_process, addr, permissions_type, MemoryPermission::DontCare);
+ return shared_memory->Map(*current_process, addr, permissions_type, MemoryPermission::DontCare);
}
static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64 size) {
@@ -724,61 +1117,82 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
shared_memory_handle, addr, size);
if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address is not aligned to 4KB, addr=0x{:016X}", addr);
return ERR_INVALID_ADDRESS;
}
- if (size == 0 || !Common::Is4KBAligned(size)) {
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, size=0x{:016X}", size);
return ERR_INVALID_SIZE;
}
if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not a valid address range, addr=0x{:016X}, size=0x{:016X}",
+ addr, size);
return ERR_INVALID_ADDRESS_STATE;
}
auto* const current_process = Core::CurrentProcess();
auto shared_memory = current_process->GetHandleTable().Get<SharedMemory>(shared_memory_handle);
if (!shared_memory) {
+ LOG_ERROR(Kernel_SVC, "Shared memory does not exist, shared_memory_handle=0x{:08X}",
+ shared_memory_handle);
return ERR_INVALID_HANDLE;
}
const auto& vm_manager = current_process->VMManager();
if (!vm_manager.IsWithinASLRRegion(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Region is not within the ASLR region. addr=0x{:016X}, size={:016X}",
+ addr, size);
return ERR_INVALID_MEMORY_RANGE;
}
- return shared_memory->Unmap(current_process, addr);
+ return shared_memory->Unmap(*current_process, addr);
}
-/// Query process memory
-static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
- Handle process_handle, u64 addr) {
+static ResultCode QueryProcessMemory(VAddr memory_info_address, VAddr page_info_address,
+ Handle process_handle, VAddr address) {
+ LOG_TRACE(Kernel_SVC, "called process=0x{:08X} address={:X}", process_handle, address);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
SharedPtr<Process> process = handle_table.Get<Process>(process_handle);
if (!process) {
+ LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
+ process_handle);
return ERR_INVALID_HANDLE;
}
- auto vma = process->VMManager().FindVMA(addr);
- memory_info->attributes = 0;
- if (vma == process->VMManager().vma_map.end()) {
- memory_info->base_address = 0;
- memory_info->permission = static_cast<u32>(VMAPermission::None);
- memory_info->size = 0;
- memory_info->type = static_cast<u32>(MemoryState::Unmapped);
- } else {
- memory_info->base_address = vma->second.base;
- memory_info->permission = static_cast<u32>(vma->second.permissions);
- memory_info->size = vma->second.size;
- memory_info->type = static_cast<u32>(vma->second.meminfo_state);
- }
- LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, addr);
+ const auto& vm_manager = process->VMManager();
+ const MemoryInfo memory_info = vm_manager.QueryMemory(address);
+
+ Memory::Write64(memory_info_address, memory_info.base_address);
+ Memory::Write64(memory_info_address + 8, memory_info.size);
+ Memory::Write32(memory_info_address + 16, memory_info.state);
+ Memory::Write32(memory_info_address + 20, memory_info.attributes);
+ Memory::Write32(memory_info_address + 24, memory_info.permission);
+ Memory::Write32(memory_info_address + 32, memory_info.ipc_ref_count);
+ Memory::Write32(memory_info_address + 28, memory_info.device_ref_count);
+ Memory::Write32(memory_info_address + 36, 0);
+
+ // Page info appears to be currently unused by the kernel and is always set to zero.
+ Memory::Write32(page_info_address, 0);
+
return RESULT_SUCCESS;
}
-/// Query memory
-static ResultCode QueryMemory(MemoryInfo* memory_info, PageInfo* page_info, VAddr addr) {
- LOG_TRACE(Kernel_SVC, "called, addr={:X}", addr);
- return QueryProcessMemory(memory_info, page_info, CurrentProcess, addr);
+static ResultCode QueryMemory(VAddr memory_info_address, VAddr page_info_address,
+ VAddr query_address) {
+ LOG_TRACE(Kernel_SVC,
+ "called, memory_info_address=0x{:016X}, page_info_address=0x{:016X}, "
+ "query_address=0x{:016X}",
+ memory_info_address, page_info_address, query_address);
+
+ return QueryProcessMemory(memory_info_address, page_info_address, CurrentProcess,
+ query_address);
}
/// Exits the current process
@@ -800,33 +1214,42 @@ static void ExitProcess() {
/// Creates a new thread
static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, VAddr stack_top,
u32 priority, s32 processor_id) {
- if (priority > THREADPRIO_LOWEST) {
- return ERR_INVALID_THREAD_PRIORITY;
- }
+ LOG_TRACE(Kernel_SVC,
+ "called entrypoint=0x{:08X}, arg=0x{:08X}, stacktop=0x{:08X}, "
+ "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
+ entry_point, arg, stack_top, priority, processor_id, *out_handle);
auto* const current_process = Core::CurrentProcess();
- const ResourceLimit& resource_limit = current_process->GetResourceLimit();
- if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
- return ERR_INVALID_THREAD_PRIORITY;
- }
- if (processor_id == THREADPROCESSORID_DEFAULT) {
- // Set the target CPU to the one specified in the process' exheader.
- processor_id = current_process->GetDefaultProcessorID();
- ASSERT(processor_id != THREADPROCESSORID_DEFAULT);
+ if (processor_id == THREADPROCESSORID_IDEAL) {
+ // Set the target CPU to the one specified by the process.
+ processor_id = current_process->GetIdealCore();
+ ASSERT(processor_id != THREADPROCESSORID_IDEAL);
}
- switch (processor_id) {
- case THREADPROCESSORID_0:
- case THREADPROCESSORID_1:
- case THREADPROCESSORID_2:
- case THREADPROCESSORID_3:
- break;
- default:
+ if (processor_id < THREADPROCESSORID_0 || processor_id > THREADPROCESSORID_3) {
LOG_ERROR(Kernel_SVC, "Invalid thread processor ID: {}", processor_id);
return ERR_INVALID_PROCESSOR_ID;
}
+ const u64 core_mask = current_process->GetCoreMask();
+ if ((core_mask | (1ULL << processor_id)) != core_mask) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread core specified ({})", processor_id);
+ return ERR_INVALID_PROCESSOR_ID;
+ }
+
+ if (priority > THREADPRIO_LOWEST) {
+ LOG_ERROR(Kernel_SVC,
+ "Invalid thread priority specified ({}). Must be within the range 0-64",
+ priority);
+ return ERR_INVALID_THREAD_PRIORITY;
+ }
+
+ if (((1ULL << priority) & current_process->GetPriorityMask()) == 0) {
+ LOG_ERROR(Kernel_SVC, "Invalid thread priority specified ({})", priority);
+ return ERR_INVALID_THREAD_PRIORITY;
+ }
+
const std::string name = fmt::format("thread-{:X}", entry_point);
auto& kernel = Core::System::GetInstance().Kernel();
CASCADE_RESULT(SharedPtr<Thread> thread,
@@ -835,6 +1258,8 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
const auto new_guest_handle = current_process->GetHandleTable().Create(thread);
if (new_guest_handle.Failed()) {
+ LOG_ERROR(Kernel_SVC, "Failed to create handle with error=0x{:X}",
+ new_guest_handle.Code().raw);
return new_guest_handle.Code();
}
thread->SetGuestHandle(*new_guest_handle);
@@ -842,11 +1267,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
- LOG_TRACE(Kernel_SVC,
- "called entrypoint=0x{:08X} ({}), arg=0x{:08X}, stacktop=0x{:08X}, "
- "threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
- entry_point, name, arg, stack_top, priority, processor_id, *out_handle);
-
return RESULT_SUCCESS;
}
@@ -857,13 +1277,18 @@ static ResultCode StartThread(Handle thread_handle) {
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
ASSERT(thread->GetStatus() == ThreadStatus::Dormant);
thread->ResumeFromWait();
- Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+
+ if (thread->GetStatus() == ThreadStatus::Ready) {
+ Core::System::GetInstance().CpuCore(thread->GetProcessorID()).PrepareReschedule();
+ }
return RESULT_SUCCESS;
}
@@ -880,18 +1305,38 @@ static void ExitThread() {
static void SleepThread(s64 nanoseconds) {
LOG_TRACE(Kernel_SVC, "called nanoseconds={}", nanoseconds);
- // Don't attempt to yield execution if there are no available threads to run,
- // this way we avoid a useless reschedule to the idle thread.
- if (nanoseconds == 0 && !Core::System::GetInstance().CurrentScheduler().HaveReadyThreads())
- return;
+ enum class SleepType : s64 {
+ YieldWithoutLoadBalancing = 0,
+ YieldWithLoadBalancing = -1,
+ YieldAndWaitForLoadBalancing = -2,
+ };
- // Sleep current thread and check for next thread to schedule
- WaitCurrentThread_Sleep();
+ if (nanoseconds <= 0) {
+ auto& scheduler{Core::System::GetInstance().CurrentScheduler()};
+ switch (static_cast<SleepType>(nanoseconds)) {
+ case SleepType::YieldWithoutLoadBalancing:
+ scheduler.YieldWithoutLoadBalancing(GetCurrentThread());
+ break;
+ case SleepType::YieldWithLoadBalancing:
+ scheduler.YieldWithLoadBalancing(GetCurrentThread());
+ break;
+ case SleepType::YieldAndWaitForLoadBalancing:
+ scheduler.YieldAndWaitForLoadBalancing(GetCurrentThread());
+ break;
+ default:
+ UNREACHABLE_MSG("Unimplemented sleep yield type '{:016X}'!", nanoseconds);
+ }
+ } else {
+ // Sleep current thread and check for next thread to schedule
+ WaitCurrentThread_Sleep();
- // Create an event to wake the thread up after the specified nanosecond delay has passed
- GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ // Create an event to wake the thread up after the specified nanosecond delay has passed
+ GetCurrentThread()->WakeAfterDelay(nanoseconds);
+ }
- Core::System::GetInstance().PrepareReschedule();
+ // Reschedule all CPU cores
+ for (std::size_t i = 0; i < Core::NUM_CPU_CORES; ++i)
+ Core::System::GetInstance().CpuCore(i).PrepareReschedule();
}
/// Wait process wide key atomic
@@ -1036,10 +1481,12 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
address, type, value, timeout);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
// If the address is not properly aligned to 4 bytes, return invalid address.
- if (address % sizeof(u32) != 0) {
+ if (!Common::IsWordAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
@@ -1051,6 +1498,10 @@ static ResultCode WaitForAddress(VAddr address, u32 type, s32 value, s64 timeout
case AddressArbiter::ArbitrationType::WaitIfEqual:
return AddressArbiter::WaitForAddressIfEqual(address, value, timeout);
default:
+ LOG_ERROR(Kernel_SVC,
+ "Invalid arbitration type, expected WaitIfLessThan, DecrementAndWaitIfLessThan "
+ "or WaitIfEqual but got {}",
+ type);
return ERR_INVALID_ENUM_VALUE;
}
}
@@ -1061,10 +1512,12 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
address, type, value, num_to_wake);
// If the passed address is a kernel virtual address, return invalid memory state.
if (Memory::IsKernelVirtualAddress(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is a kernel virtual address, address={:016X}", address);
return ERR_INVALID_ADDRESS_STATE;
}
// If the address is not properly aligned to 4 bytes, return invalid address.
- if (address % sizeof(u32) != 0) {
+ if (!Common::IsWordAligned(address)) {
+ LOG_ERROR(Kernel_SVC, "Address is not word aligned, address={:016X}", address);
return ERR_INVALID_ADDRESS;
}
@@ -1077,12 +1530,18 @@ static ResultCode SignalToAddress(VAddr address, u32 type, s32 value, s32 num_to
return AddressArbiter::ModifyByWaitingCountAndSignalToAddressIfEqual(address, value,
num_to_wake);
default:
+ LOG_ERROR(Kernel_SVC,
+ "Invalid signal type, expected Signal, IncrementAndSignalIfEqual "
+ "or ModifyByWaitingCountAndSignalIfEqual but got {}",
+ type);
return ERR_INVALID_ENUM_VALUE;
}
}
/// This returns the total CPU ticks elapsed since the CPU was powered-on
static u64 GetSystemTick() {
+ LOG_TRACE(Kernel_SVC, "called");
+
const u64 result{CoreTiming::GetTicks()};
// Advance time to defeat dumb games that busy-wait for the frame to end.
@@ -1099,24 +1558,61 @@ static ResultCode CloseHandle(Handle handle) {
return handle_table.Close(handle);
}
-/// Reset an event
+/// Clears the signaled state of an event or process.
static ResultCode ResetSignal(Handle handle) {
- LOG_WARNING(Kernel_SVC, "(STUBBED) called handle 0x{:08X}", handle);
+ LOG_DEBUG(Kernel_SVC, "called handle 0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- auto event = handle_table.Get<Event>(handle);
- ASSERT(event != nullptr);
+ auto event = handle_table.Get<ReadableEvent>(handle);
+ if (event) {
+ return event->Reset();
+ }
- event->Clear();
- return RESULT_SUCCESS;
+ auto process = handle_table.Get<Process>(handle);
+ if (process) {
+ return process->ClearSignalState();
+ }
+
+ LOG_ERROR(Kernel_SVC, "Invalid handle (0x{:08X})", handle);
+ return ERR_INVALID_HANDLE;
}
/// Creates a TransferMemory object
static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32 permissions) {
- LOG_WARNING(Kernel_SVC, "(STUBBED) called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
- permissions);
- *handle = 0;
+ LOG_DEBUG(Kernel_SVC, "called addr=0x{:X}, size=0x{:X}, perms=0x{:08X}", addr, size,
+ permissions);
+
+ if (!Common::Is4KBAligned(addr)) {
+ LOG_ERROR(Kernel_SVC, "Address ({:016X}) is not page aligned!", addr);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!Common::Is4KBAligned(size) || size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size ({:016X}) is not page aligned or equal to zero!", size);
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (!IsValidAddressRange(addr, size)) {
+ LOG_ERROR(Kernel_SVC, "Address and size cause overflow! (address={:016X}, size={:016X})",
+ addr, size);
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ const auto perms = static_cast<MemoryPermission>(permissions);
+ if (perms != MemoryPermission::None && perms != MemoryPermission::Read &&
+ perms != MemoryPermission::ReadWrite) {
+ LOG_ERROR(Kernel_SVC, "Invalid memory permissions for transfer memory! (perms={:08X})",
+ permissions);
+ return ERR_INVALID_MEMORY_PERMISSIONS;
+ }
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ auto process = kernel.CurrentProcess();
+ auto& handle_table = process->GetHandleTable();
+ const auto shared_mem_handle = SharedMemory::Create(kernel, process, size, perms, perms, addr);
+
+ CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
return RESULT_SUCCESS;
}
@@ -1126,6 +1622,8 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
@@ -1136,27 +1634,30 @@ static ResultCode GetThreadCoreMask(Handle thread_handle, u32* core, u64* mask)
}
static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
- LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:16X}, core=0x{:X}", thread_handle,
+ LOG_DEBUG(Kernel_SVC, "called, handle=0x{:08X}, mask=0x{:016X}, core=0x{:X}", thread_handle,
mask, core);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const SharedPtr<Thread> thread = handle_table.Get<Thread>(thread_handle);
if (!thread) {
+ LOG_ERROR(Kernel_SVC, "Thread handle does not exist, thread_handle=0x{:08X}",
+ thread_handle);
return ERR_INVALID_HANDLE;
}
- if (core == static_cast<u32>(THREADPROCESSORID_DEFAULT)) {
- const u8 default_processor_id = thread->GetOwnerProcess()->GetDefaultProcessorID();
+ if (core == static_cast<u32>(THREADPROCESSORID_IDEAL)) {
+ const u8 ideal_cpu_core = thread->GetOwnerProcess()->GetIdealCore();
- ASSERT(default_processor_id != static_cast<u8>(THREADPROCESSORID_DEFAULT));
+ ASSERT(ideal_cpu_core != static_cast<u8>(THREADPROCESSORID_IDEAL));
- // Set the target CPU to the one specified in the process' exheader.
- core = default_processor_id;
+ // Set the target CPU to the ideal core specified by the process.
+ core = ideal_cpu_core;
mask = 1ULL << core;
}
if (mask == 0) {
- return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
+ LOG_ERROR(Kernel_SVC, "Mask is 0");
+ return ERR_INVALID_COMBINATION;
}
/// This value is used to only change the affinity mask without changing the current ideal core.
@@ -1165,12 +1666,15 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
if (core == OnlyChangeMask) {
core = thread->GetIdealCore();
} else if (core >= Core::NUM_CPU_CORES && core != static_cast<u32>(-1)) {
- return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidProcessorId);
+ LOG_ERROR(Kernel_SVC, "Invalid core specified, got {}", core);
+ return ERR_INVALID_PROCESSOR_ID;
}
// Error out if the input core isn't enabled in the input mask.
if (core < Core::NUM_CPU_CORES && (mask & (1ull << core)) == 0) {
- return ResultCode(ErrorModule::Kernel, ErrCodes::InvalidCombination);
+ LOG_ERROR(Kernel_SVC, "Core is not enabled for the current mask, core={}, mask={:016X}",
+ core, mask);
+ return ERR_INVALID_COMBINATION;
}
thread->ChangeCore(core, mask);
@@ -1182,44 +1686,109 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
u32 remote_permissions) {
LOG_TRACE(Kernel_SVC, "called, size=0x{:X}, localPerms=0x{:08X}, remotePerms=0x{:08X}", size,
local_permissions, remote_permissions);
+ if (size == 0) {
+ LOG_ERROR(Kernel_SVC, "Size is 0");
+ return ERR_INVALID_SIZE;
+ }
+ if (!Common::Is4KBAligned(size)) {
+ LOG_ERROR(Kernel_SVC, "Size is not aligned to 4KB, 0x{:016X}", size);
+ return ERR_INVALID_SIZE;
+ }
- // Size must be a multiple of 4KB and be less than or equal to
- // approx. 8 GB (actually (1GB - 512B) * 8)
- if (size == 0 || (size & 0xFFFFFFFE00000FFF) != 0) {
+ if (size >= MAIN_MEMORY_SIZE) {
+ LOG_ERROR(Kernel_SVC, "Size is not less than 8GB, 0x{:016X}", size);
return ERR_INVALID_SIZE;
}
const auto local_perms = static_cast<MemoryPermission>(local_permissions);
if (local_perms != MemoryPermission::Read && local_perms != MemoryPermission::ReadWrite) {
+ LOG_ERROR(Kernel_SVC,
+ "Invalid local memory permissions, expected Read or ReadWrite but got "
+ "local_permissions={}",
+ static_cast<u32>(local_permissions));
return ERR_INVALID_MEMORY_PERMISSIONS;
}
const auto remote_perms = static_cast<MemoryPermission>(remote_permissions);
if (remote_perms != MemoryPermission::Read && remote_perms != MemoryPermission::ReadWrite &&
remote_perms != MemoryPermission::DontCare) {
+ LOG_ERROR(Kernel_SVC,
+ "Invalid remote memory permissions, expected Read, ReadWrite or DontCare but got "
+ "remote_permissions={}",
+ static_cast<u32>(remote_permissions));
return ERR_INVALID_MEMORY_PERMISSIONS;
}
auto& kernel = Core::System::GetInstance().Kernel();
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- auto shared_mem_handle =
- SharedMemory::Create(kernel, handle_table.Get<Process>(KernelHandle::CurrentProcess), size,
- local_perms, remote_perms);
+ auto process = kernel.CurrentProcess();
+ auto& handle_table = process->GetHandleTable();
+ auto shared_mem_handle = SharedMemory::Create(kernel, process, size, local_perms, remote_perms);
CASCADE_RESULT(*handle, handle_table.Create(shared_mem_handle));
return RESULT_SUCCESS;
}
+static ResultCode CreateEvent(Handle* write_handle, Handle* read_handle) {
+ LOG_DEBUG(Kernel_SVC, "called");
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ const auto [readable_event, writable_event] =
+ WritableEvent::CreateEventPair(kernel, ResetType::Sticky, "CreateEvent");
+
+ HandleTable& handle_table = kernel.CurrentProcess()->GetHandleTable();
+
+ const auto write_create_result = handle_table.Create(writable_event);
+ if (write_create_result.Failed()) {
+ return write_create_result.Code();
+ }
+ *write_handle = *write_create_result;
+
+ const auto read_create_result = handle_table.Create(readable_event);
+ if (read_create_result.Failed()) {
+ handle_table.Close(*write_create_result);
+ return read_create_result.Code();
+ }
+ *read_handle = *read_create_result;
+
+ LOG_DEBUG(Kernel_SVC,
+ "successful. Writable event handle=0x{:08X}, Readable event handle=0x{:08X}",
+ *write_create_result, *read_create_result);
+ return RESULT_SUCCESS;
+}
+
static ResultCode ClearEvent(Handle handle) {
LOG_TRACE(Kernel_SVC, "called, event=0x{:08X}", handle);
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- SharedPtr<Event> evt = handle_table.Get<Event>(handle);
- if (evt == nullptr) {
+
+ auto writable_event = handle_table.Get<WritableEvent>(handle);
+ if (writable_event) {
+ writable_event->Clear();
+ return RESULT_SUCCESS;
+ }
+
+ auto readable_event = handle_table.Get<ReadableEvent>(handle);
+ if (readable_event) {
+ readable_event->Clear();
+ return RESULT_SUCCESS;
+ }
+
+ LOG_ERROR(Kernel_SVC, "Event handle does not exist, handle=0x{:08X}", handle);
+ return ERR_INVALID_HANDLE;
+}
+
+static ResultCode SignalEvent(Handle handle) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle=0x{:08X}", handle);
+
+ HandleTable& handle_table = Core::CurrentProcess()->GetHandleTable();
+ auto writable_event = handle_table.Get<WritableEvent>(handle);
+
+ if (!writable_event) {
+ LOG_ERROR(Kernel_SVC, "Non-existent writable event handle used (0x{:08X})", handle);
return ERR_INVALID_HANDLE;
}
- evt->Clear();
+ writable_event->Signal();
return RESULT_SUCCESS;
}
@@ -1234,11 +1803,14 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
const auto& handle_table = Core::CurrentProcess()->GetHandleTable();
const auto process = handle_table.Get<Process>(process_handle);
if (!process) {
+ LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
+ process_handle);
return ERR_INVALID_HANDLE;
}
const auto info_type = static_cast<InfoType>(type);
if (info_type != InfoType::Status) {
+ LOG_ERROR(Kernel_SVC, "Expected info_type to be Status but got {} instead", type);
return ERR_INVALID_ENUM_VALUE;
}
@@ -1246,6 +1818,87 @@ static ResultCode GetProcessInfo(u64* out, Handle process_handle, u32 type) {
return RESULT_SUCCESS;
}
+static ResultCode CreateResourceLimit(Handle* out_handle) {
+ LOG_DEBUG(Kernel_SVC, "called");
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ auto resource_limit = ResourceLimit::Create(kernel);
+
+ auto* const current_process = kernel.CurrentProcess();
+ ASSERT(current_process != nullptr);
+
+ const auto handle = current_process->GetHandleTable().Create(std::move(resource_limit));
+ if (handle.Failed()) {
+ return handle.Code();
+ }
+
+ *out_handle = *handle;
+ return RESULT_SUCCESS;
+}
+
+static ResultCode GetResourceLimitLimitValue(u64* out_value, Handle resource_limit,
+ u32 resource_type) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
+
+ const auto limit_value = RetrieveResourceLimitValue(resource_limit, resource_type,
+ ResourceLimitValueType::LimitValue);
+ if (limit_value.Failed()) {
+ return limit_value.Code();
+ }
+
+ *out_value = static_cast<u64>(*limit_value);
+ return RESULT_SUCCESS;
+}
+
+static ResultCode GetResourceLimitCurrentValue(u64* out_value, Handle resource_limit,
+ u32 resource_type) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}", resource_limit, resource_type);
+
+ const auto current_value = RetrieveResourceLimitValue(resource_limit, resource_type,
+ ResourceLimitValueType::CurrentValue);
+ if (current_value.Failed()) {
+ return current_value.Code();
+ }
+
+ *out_value = static_cast<u64>(*current_value);
+ return RESULT_SUCCESS;
+}
+
+static ResultCode SetResourceLimitLimitValue(Handle resource_limit, u32 resource_type, u64 value) {
+ LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit,
+ resource_type, value);
+
+ const auto type = static_cast<ResourceType>(resource_type);
+ if (!IsValidResourceType(type)) {
+ LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type);
+ return ERR_INVALID_ENUM_VALUE;
+ }
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ auto* const current_process = kernel.CurrentProcess();
+ ASSERT(current_process != nullptr);
+
+ auto resource_limit_object =
+ current_process->GetHandleTable().Get<ResourceLimit>(resource_limit);
+ if (!resource_limit_object) {
+ LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}",
+ resource_limit);
+ return ERR_INVALID_HANDLE;
+ }
+
+ const auto set_result = resource_limit_object->SetLimitValue(type, static_cast<s64>(value));
+ if (set_result.IsError()) {
+ LOG_ERROR(
+ Kernel_SVC,
+ "Attempted to lower resource limit ({}) for category '{}' below its current value ({})",
+ resource_limit_object->GetMaxResourceValue(type), resource_type,
+ resource_limit_object->GetCurrentResourceValue(type));
+ return set_result;
+ }
+
+ return RESULT_SUCCESS;
+}
+
namespace {
struct FunctionDef {
using Func = void();
@@ -1259,7 +1912,7 @@ struct FunctionDef {
static const FunctionDef SVC_Table[] = {
{0x00, nullptr, "Unknown"},
{0x01, SvcWrap<SetHeapSize>, "SetHeapSize"},
- {0x02, nullptr, "SetMemoryPermission"},
+ {0x02, SvcWrap<SetMemoryPermission>, "SetMemoryPermission"},
{0x03, SvcWrap<SetMemoryAttribute>, "SetMemoryAttribute"},
{0x04, SvcWrap<MapMemory>, "MapMemory"},
{0x05, SvcWrap<UnmapMemory>, "UnmapMemory"},
@@ -1274,7 +1927,7 @@ static const FunctionDef SVC_Table[] = {
{0x0E, SvcWrap<GetThreadCoreMask>, "GetThreadCoreMask"},
{0x0F, SvcWrap<SetThreadCoreMask>, "SetThreadCoreMask"},
{0x10, SvcWrap<GetCurrentProcessorNumber>, "GetCurrentProcessorNumber"},
- {0x11, nullptr, "SignalEvent"},
+ {0x11, SvcWrap<SignalEvent>, "SignalEvent"},
{0x12, SvcWrap<ClearEvent>, "ClearEvent"},
{0x13, SvcWrap<MapSharedMemory>, "MapSharedMemory"},
{0x14, SvcWrap<UnmapSharedMemory>, "UnmapSharedMemory"},
@@ -1305,8 +1958,8 @@ static const FunctionDef SVC_Table[] = {
{0x2D, nullptr, "UnmapPhysicalMemory"},
{0x2E, nullptr, "GetFutureThreadInfo"},
{0x2F, nullptr, "GetLastThreadInfo"},
- {0x30, nullptr, "GetResourceLimitLimitValue"},
- {0x31, nullptr, "GetResourceLimitCurrentValue"},
+ {0x30, SvcWrap<GetResourceLimitLimitValue>, "GetResourceLimitLimitValue"},
+ {0x31, SvcWrap<GetResourceLimitCurrentValue>, "GetResourceLimitCurrentValue"},
{0x32, SvcWrap<SetThreadActivity>, "SetThreadActivity"},
{0x33, SvcWrap<GetThreadContext>, "GetThreadContext"},
{0x34, SvcWrap<WaitForAddress>, "WaitForAddress"},
@@ -1326,7 +1979,7 @@ static const FunctionDef SVC_Table[] = {
{0x42, nullptr, "ReplyAndReceiveLight"},
{0x43, nullptr, "ReplyAndReceive"},
{0x44, nullptr, "ReplyAndReceiveWithUserBuffer"},
- {0x45, nullptr, "CreateEvent"},
+ {0x45, SvcWrap<CreateEvent>, "CreateEvent"},
{0x46, nullptr, "Unknown"},
{0x47, nullptr, "Unknown"},
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
@@ -1375,15 +2028,15 @@ static const FunctionDef SVC_Table[] = {
{0x73, nullptr, "SetProcessMemoryPermission"},
{0x74, nullptr, "MapProcessMemory"},
{0x75, nullptr, "UnmapProcessMemory"},
- {0x76, nullptr, "QueryProcessMemory"},
+ {0x76, SvcWrap<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, nullptr, "MapProcessCodeMemory"},
{0x78, nullptr, "UnmapProcessCodeMemory"},
{0x79, nullptr, "CreateProcess"},
{0x7A, nullptr, "StartProcess"},
{0x7B, nullptr, "TerminateProcess"},
{0x7C, SvcWrap<GetProcessInfo>, "GetProcessInfo"},
- {0x7D, nullptr, "CreateResourceLimit"},
- {0x7E, nullptr, "SetResourceLimitLimitValue"},
+ {0x7D, SvcWrap<CreateResourceLimit>, "CreateResourceLimit"},
+ {0x7E, SvcWrap<SetResourceLimitLimitValue>, "SetResourceLimitLimitValue"},
{0x7F, nullptr, "CallSecureMonitor"},
};
diff --git a/src/core/hle/kernel/svc.h b/src/core/hle/kernel/svc.h
index b06aac4ec..c37ae0f98 100644
--- a/src/core/hle/kernel/svc.h
+++ b/src/core/hle/kernel/svc.h
@@ -8,22 +8,6 @@
namespace Kernel {
-struct MemoryInfo {
- u64 base_address;
- u64 size;
- u32 type;
- u32 attributes;
- u32 permission;
- u32 device_refcount;
- u32 ipc_refcount;
- INSERT_PADDING_WORDS(1);
-};
-static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size.");
-
-struct PageInfo {
- u64 flags;
-};
-
void CallSVC(u32 immediate);
} // namespace Kernel
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index b09753c80..2a2c2c5ea 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -7,9 +7,7 @@
#include "common/common_types.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
-#include "core/hle/kernel/svc.h"
#include "core/hle/result.h"
-#include "core/memory.h"
namespace Kernel {
@@ -43,6 +41,14 @@ void SvcWrap() {
FuncReturn(func(static_cast<u32>(Param(0)), static_cast<u32>(Param(1))).raw);
}
+template <ResultCode func(u32*)>
+void SvcWrap() {
+ u32 param = 0;
+ const u32 retval = func(&param).raw;
+ Core::CurrentArmInterface().SetReg(1, param);
+ FuncReturn(retval);
+}
+
template <ResultCode func(u32*, u32)>
void SvcWrap() {
u32 param_1 = 0;
@@ -51,10 +57,31 @@ void SvcWrap() {
FuncReturn(retval);
}
+template <ResultCode func(u32*, u32*)>
+void SvcWrap() {
+ u32 param_1 = 0;
+ u32 param_2 = 0;
+ const u32 retval = func(&param_1, &param_2).raw;
+
+ auto& arm_interface = Core::CurrentArmInterface();
+ arm_interface.SetReg(1, param_1);
+ arm_interface.SetReg(2, param_2);
+
+ FuncReturn(retval);
+}
+
template <ResultCode func(u32*, u64)>
void SvcWrap() {
u32 param_1 = 0;
- u32 retval = func(&param_1, Param(1)).raw;
+ const u32 retval = func(&param_1, Param(1)).raw;
+ Core::CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(retval);
+}
+
+template <ResultCode func(u64*, u32)>
+void SvcWrap() {
+ u64 param_1 = 0;
+ const u32 retval = func(&param_1, static_cast<u32>(Param(1))).raw;
Core::CurrentArmInterface().SetReg(1, param_1);
FuncReturn(retval);
}
@@ -108,7 +135,12 @@ void SvcWrap() {
template <ResultCode func(u64, u64, u32, u32)>
void SvcWrap() {
FuncReturn(
- func(Param(0), Param(1), static_cast<u32>(Param(3)), static_cast<u32>(Param(3))).raw);
+ func(Param(0), Param(1), static_cast<u32>(Param(2)), static_cast<u32>(Param(3))).raw);
+}
+
+template <ResultCode func(u64, u64, u32, u64)>
+void SvcWrap() {
+ FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2)), Param(3)).raw);
}
template <ResultCode func(u32, u64, u32)>
@@ -121,6 +153,11 @@ void SvcWrap() {
FuncReturn(func(Param(0), Param(1), Param(2)).raw);
}
+template <ResultCode func(u64, u64, u32)>
+void SvcWrap() {
+ FuncReturn(func(Param(0), Param(1), static_cast<u32>(Param(2))).raw);
+}
+
template <ResultCode func(u32, u64, u64, u32)>
void SvcWrap() {
FuncReturn(
@@ -165,21 +202,6 @@ void SvcWrap() {
FuncReturn(retval);
}
-template <ResultCode func(MemoryInfo*, PageInfo*, u64)>
-void SvcWrap() {
- MemoryInfo memory_info = {};
- PageInfo page_info = {};
- u32 retval = func(&memory_info, &page_info, Param(2)).raw;
-
- Memory::Write64(Param(0), memory_info.base_address);
- Memory::Write64(Param(0) + 8, memory_info.size);
- Memory::Write32(Param(0) + 16, memory_info.type);
- Memory::Write32(Param(0) + 20, memory_info.attributes);
- Memory::Write32(Param(0) + 24, memory_info.permission);
-
- FuncReturn(retval);
-}
-
template <ResultCode func(u32*, u64, u64, u32)>
void SvcWrap() {
u32 param_1 = 0;
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index 59bc9e0af..d3984dfc4 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -4,15 +4,14 @@
#include <algorithm>
#include <cinttypes>
+#include <optional>
#include <vector>
-#include <boost/optional.hpp>
#include <boost/range/algorithm_ext/erase.hpp>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "common/math_util.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
@@ -50,7 +49,7 @@ void Thread::Stop() {
// Clean up thread from ready queue
// This is only needed when the thread is terminated forcefully (SVC TerminateProcess)
- if (status == ThreadStatus::Ready) {
+ if (status == ThreadStatus::Ready || status == ThreadStatus::Paused) {
scheduler->UnscheduleThread(this, current_priority);
}
@@ -94,7 +93,7 @@ void Thread::CancelWakeupTimer() {
CoreTiming::UnscheduleEventThreadsafe(kernel.ThreadWakeupCallbackEventType(), callback_handle);
}
-static boost::optional<s32> GetNextProcessorId(u64 mask) {
+static std::optional<s32> GetNextProcessorId(u64 mask) {
for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) {
if (mask & (1ULL << index)) {
if (!Core::System::GetInstance().Scheduler(index).GetCurrentThread()) {
@@ -140,38 +139,14 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
- status = ThreadStatus::Ready;
-
- boost::optional<s32> new_processor_id = GetNextProcessorId(affinity_mask);
- if (!new_processor_id) {
- new_processor_id = processor_id;
- }
- if (ideal_core != -1 &&
- Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
- new_processor_id = ideal_core;
- }
-
- ASSERT(*new_processor_id < 4);
-
- // Add thread to new core's scheduler
- auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
-
- if (*new_processor_id != processor_id) {
- // Remove thread from previous core's scheduler
- scheduler->RemoveThread(this);
- next_scheduler->AddThread(this, current_priority);
+ if (activity == ThreadActivity::Paused) {
+ status = ThreadStatus::Paused;
+ return;
}
- processor_id = *new_processor_id;
-
- // If the thread was ready, unschedule from the previous core and schedule on the new core
- scheduler->UnscheduleThread(this, current_priority);
- next_scheduler->ScheduleThread(this, current_priority);
-
- // Change thread's scheduler
- scheduler = next_scheduler;
+ status = ThreadStatus::Ready;
- Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
+ ChangeScheduler();
}
/**
@@ -187,6 +162,9 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, VAdd
context.cpu_registers[0] = arg;
context.pc = entry_point;
context.sp = stack_top;
+ // TODO(merry): Perform a hardware test to determine the below value.
+ // AHP = 0, DN = 1, FTZ = 1, RMode = Round towards zero
+ context.fpcr = 0x03C00000;
}
ResultVal<SharedPtr<Thread>> Thread::Create(KernelCore& kernel, std::string name, VAddr entry_point,
@@ -253,29 +231,6 @@ void Thread::BoostPriority(u32 priority) {
current_priority = priority;
}
-SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
- Process& owner_process) {
- // Setup page table so we can write to memory
- SetCurrentPageTable(&owner_process.VMManager().page_table);
-
- // Initialize new "main" thread
- const VAddr stack_top = owner_process.VMManager().GetTLSIORegionEndAddress();
- auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0,
- stack_top, owner_process);
-
- SharedPtr<Thread> thread = std::move(thread_res).Unwrap();
-
- // Register 1 must be a handle to the main thread
- const Handle guest_handle = owner_process.GetHandleTable().Create(thread).Unwrap();
- thread->SetGuestHandle(guest_handle);
- thread->GetContext().cpu_registers[1] = guest_handle;
-
- // Threads by default are dormant, wake up the main thread so it runs when the scheduler fires
- thread->ResumeFromWait();
-
- return thread;
-}
-
void Thread::SetWaitSynchronizationResult(ResultCode result) {
context.cpu_registers[0] = result.raw;
}
@@ -364,42 +319,45 @@ void Thread::UpdatePriority() {
void Thread::ChangeCore(u32 core, u64 mask) {
ideal_core = core;
affinity_mask = mask;
+ ChangeScheduler();
+}
+void Thread::ChangeScheduler() {
if (status != ThreadStatus::Ready) {
return;
}
- boost::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
+ auto& system = Core::System::GetInstance();
+ std::optional<s32> new_processor_id{GetNextProcessorId(affinity_mask)};
if (!new_processor_id) {
new_processor_id = processor_id;
}
- if (ideal_core != -1 &&
- Core::System::GetInstance().Scheduler(ideal_core).GetCurrentThread() == nullptr) {
+ if (ideal_core != -1 && system.Scheduler(ideal_core).GetCurrentThread() == nullptr) {
new_processor_id = ideal_core;
}
ASSERT(*new_processor_id < 4);
// Add thread to new core's scheduler
- auto* next_scheduler = &Core::System::GetInstance().Scheduler(*new_processor_id);
+ auto& next_scheduler = system.Scheduler(*new_processor_id);
if (*new_processor_id != processor_id) {
// Remove thread from previous core's scheduler
scheduler->RemoveThread(this);
- next_scheduler->AddThread(this, current_priority);
+ next_scheduler.AddThread(this, current_priority);
}
processor_id = *new_processor_id;
// If the thread was ready, unschedule from the previous core and schedule on the new core
scheduler->UnscheduleThread(this, current_priority);
- next_scheduler->ScheduleThread(this, current_priority);
+ next_scheduler.ScheduleThread(this, current_priority);
// Change thread's scheduler
- scheduler = next_scheduler;
+ scheduler = &next_scheduler;
- Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
+ system.CpuCore(processor_id).PrepareReschedule();
}
bool Thread::AllWaitObjectsReady() {
@@ -414,6 +372,23 @@ bool Thread::InvokeWakeupCallback(ThreadWakeupReason reason, SharedPtr<Thread> t
return wakeup_callback(reason, std::move(thread), std::move(object), index);
}
+void Thread::SetActivity(ThreadActivity value) {
+ activity = value;
+
+ if (value == ThreadActivity::Paused) {
+ // Set status if not waiting
+ if (status == ThreadStatus::Ready) {
+ status = ThreadStatus::Paused;
+ } else if (status == ThreadStatus::Running) {
+ status = ThreadStatus::Paused;
+ Core::System::GetInstance().CpuCore(processor_id).PrepareReschedule();
+ }
+ } else if (status == ThreadStatus::Paused) {
+ // Ready to reschedule
+ ResumeFromWait();
+ }
+}
+
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
diff --git a/src/core/hle/kernel/thread.h b/src/core/hle/kernel/thread.h
index 4a6e11239..c48b21aba 100644
--- a/src/core/hle/kernel/thread.h
+++ b/src/core/hle/kernel/thread.h
@@ -26,15 +26,16 @@ enum ThreadPriority : u32 {
THREADPRIO_USERLAND_MAX = 24, ///< Highest thread priority for userland apps
THREADPRIO_DEFAULT = 44, ///< Default thread priority for userland apps
THREADPRIO_LOWEST = 63, ///< Lowest thread priority
+ THREADPRIO_COUNT = 64, ///< Total number of possible thread priorities.
};
enum ThreadProcessorId : s32 {
- THREADPROCESSORID_DEFAULT = -2, ///< Run thread on default core specified by exheader
- THREADPROCESSORID_0 = 0, ///< Run thread on core 0
- THREADPROCESSORID_1 = 1, ///< Run thread on core 1
- THREADPROCESSORID_2 = 2, ///< Run thread on core 2
- THREADPROCESSORID_3 = 3, ///< Run thread on core 3
- THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
+ THREADPROCESSORID_IDEAL = -2, ///< Run thread on the ideal core specified by the process.
+ THREADPROCESSORID_0 = 0, ///< Run thread on core 0
+ THREADPROCESSORID_1 = 1, ///< Run thread on core 1
+ THREADPROCESSORID_2 = 2, ///< Run thread on core 2
+ THREADPROCESSORID_3 = 3, ///< Run thread on core 3
+ THREADPROCESSORID_MAX = 4, ///< Processor ID must be less than this
/// Allowed CPU mask
THREADPROCESSORID_DEFAULT_MASK = (1 << THREADPROCESSORID_0) | (1 << THREADPROCESSORID_1) |
@@ -44,6 +45,7 @@ enum ThreadProcessorId : s32 {
enum class ThreadStatus {
Running, ///< Currently running
Ready, ///< Ready to run
+ Paused, ///< Paused by SetThreadActivity or debug
WaitHLEEvent, ///< Waiting for hle event to finish
WaitSleep, ///< Waiting due to a SleepThread SVC
WaitIPC, ///< Waiting for the reply from an IPC request
@@ -60,6 +62,11 @@ enum class ThreadWakeupReason {
Timeout // The thread was woken up due to a wait timeout.
};
+enum class ThreadActivity : u32 {
+ Normal = 0,
+ Paused = 1,
+};
+
class Thread final : public WaitObject {
public:
using TLSMemory = std::vector<u8>;
@@ -150,7 +157,7 @@ public:
* Gets the thread's thread ID
* @return The thread's ID
*/
- u32 GetThreadID() const {
+ u64 GetThreadID() const {
return thread_id;
}
@@ -370,13 +377,21 @@ public:
return affinity_mask;
}
+ ThreadActivity GetActivity() const {
+ return activity;
+ }
+
+ void SetActivity(ThreadActivity value);
+
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
+ void ChangeScheduler();
+
Core::ARM_Interface::ThreadContext context{};
- u32 thread_id = 0;
+ u64 thread_id = 0;
ThreadStatus status = ThreadStatus::Dormant;
@@ -436,18 +451,9 @@ private:
TLSMemoryPtr tls_memory = std::make_shared<TLSMemory>();
std::string name;
-};
-/**
- * Sets up the primary application thread
- * @param kernel The kernel instance to create the main thread under.
- * @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<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 priority,
- Process& owner_process);
+ ThreadActivity activity = ThreadActivity::Normal;
+};
/**
* Gets the current thread
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 1a92c8f70..10ad94aa6 100644
--- a/src/core/hle/kernel/vm_manager.cpp
+++ b/src/core/hle/kernel/vm_manager.cpp
@@ -25,19 +25,19 @@ static const char* GetMemoryStateName(MemoryState state) {
"CodeMutable", "Heap",
"Shared", "Unknown1",
"ModuleCodeStatic", "ModuleCodeMutable",
- "IpcBuffer0", "Mapped",
+ "IpcBuffer0", "Stack",
"ThreadLocal", "TransferMemoryIsolated",
"TransferMemory", "ProcessMemory",
- "Unknown2", "IpcBuffer1",
+ "Inaccessible", "IpcBuffer1",
"IpcBuffer3", "KernelStack",
};
- return names[static_cast<int>(state)];
+ return names[ToSvcMemoryState(state)];
}
bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const {
ASSERT(base + size == next.base);
- if (permissions != next.permissions || meminfo_state != next.meminfo_state ||
+ if (permissions != next.permissions || state != next.state || attribute != next.attribute ||
type != next.type) {
return false;
}
@@ -87,6 +87,10 @@ VMManager::VMAHandle VMManager::FindVMA(VAddr target) const {
}
}
+bool VMManager::IsValidHandle(VMAHandle handle) const {
+ return handle != vma_map.cend();
+}
+
ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
std::shared_ptr<std::vector<u8>> block,
std::size_t offset, u64 size,
@@ -111,7 +115,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMemoryBlock(VAddr target,
final_vma.type = VMAType::AllocatedMemoryBlock;
final_vma.permissions = VMAPermission::ReadWrite;
- final_vma.meminfo_state = state;
+ final_vma.state = state;
final_vma.backing_block = std::move(block);
final_vma.offset = offset;
UpdatePageTableForVMA(final_vma);
@@ -136,7 +140,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapBackingMemory(VAddr target, u8* me
final_vma.type = VMAType::BackingMemory;
final_vma.permissions = VMAPermission::ReadWrite;
- final_vma.meminfo_state = state;
+ final_vma.state = state;
final_vma.backing_memory = memory;
UpdatePageTableForVMA(final_vma);
@@ -173,7 +177,7 @@ ResultVal<VMManager::VMAHandle> VMManager::MapMMIO(VAddr target, PAddr paddr, u6
final_vma.type = VMAType::MMIO;
final_vma.permissions = VMAPermission::ReadWrite;
- final_vma.meminfo_state = state;
+ final_vma.state = state;
final_vma.paddr = paddr;
final_vma.mmio_handler = std::move(mmio_handler);
UpdatePageTableForVMA(final_vma);
@@ -185,7 +189,8 @@ VMManager::VMAIter VMManager::Unmap(VMAIter vma_handle) {
VirtualMemoryArea& vma = vma_handle->second;
vma.type = VMAType::Free;
vma.permissions = VMAPermission::None;
- vma.meminfo_state = MemoryState::Unmapped;
+ vma.state = MemoryState::Unmapped;
+ vma.attribute = MemoryAttribute::None;
vma.backing_block = nullptr;
vma.offset = 0;
@@ -243,6 +248,133 @@ ResultCode VMManager::ReprotectRange(VAddr target, u64 size, VMAPermission new_p
return RESULT_SUCCESS;
}
+ResultVal<VAddr> VMManager::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
+ if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
+ target + size < target) {
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (heap_memory == nullptr) {
+ // Initialize heap
+ heap_memory = std::make_shared<std::vector<u8>>();
+ heap_start = heap_end = target;
+ } else {
+ UnmapRange(heap_start, heap_end - heap_start);
+ }
+
+ // If necessary, expand backing vector to cover new heap extents.
+ if (target < heap_start) {
+ heap_memory->insert(begin(*heap_memory), heap_start - target, 0);
+ heap_start = target;
+ RefreshMemoryBlockMappings(heap_memory.get());
+ }
+ if (target + size > heap_end) {
+ heap_memory->insert(end(*heap_memory), (target + size) - heap_end, 0);
+ heap_end = target + size;
+ RefreshMemoryBlockMappings(heap_memory.get());
+ }
+ ASSERT(heap_end - heap_start == heap_memory->size());
+
+ CASCADE_RESULT(auto vma, MapMemoryBlock(target, heap_memory, target - heap_start, size,
+ MemoryState::Heap));
+ Reprotect(vma, perms);
+
+ heap_used = size;
+
+ return MakeResult<VAddr>(heap_end - size);
+}
+
+ResultCode VMManager::HeapFree(VAddr target, u64 size) {
+ if (target < GetHeapRegionBaseAddress() || target + size > GetHeapRegionEndAddress() ||
+ target + size < target) {
+ return ERR_INVALID_ADDRESS;
+ }
+
+ if (size == 0) {
+ return RESULT_SUCCESS;
+ }
+
+ const ResultCode result = UnmapRange(target, size);
+ if (result.IsError()) {
+ return result;
+ }
+
+ heap_used -= size;
+ return RESULT_SUCCESS;
+}
+
+MemoryInfo VMManager::QueryMemory(VAddr address) const {
+ const auto vma = FindVMA(address);
+ MemoryInfo memory_info{};
+
+ if (IsValidHandle(vma)) {
+ memory_info.base_address = vma->second.base;
+ memory_info.attributes = ToSvcMemoryAttribute(vma->second.attribute);
+ memory_info.permission = static_cast<u32>(vma->second.permissions);
+ memory_info.size = vma->second.size;
+ memory_info.state = ToSvcMemoryState(vma->second.state);
+ } else {
+ memory_info.base_address = address_space_end;
+ memory_info.permission = static_cast<u32>(VMAPermission::None);
+ memory_info.size = 0 - address_space_end;
+ memory_info.state = static_cast<u32>(MemoryState::Inaccessible);
+ }
+
+ return memory_info;
+}
+
+ResultCode VMManager::SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
+ MemoryAttribute attribute) {
+ constexpr auto ignore_mask = MemoryAttribute::Uncached | MemoryAttribute::DeviceMapped;
+ constexpr auto attribute_mask = ~ignore_mask;
+
+ const auto result = CheckRangeState(
+ address, size, MemoryState::FlagUncached, MemoryState::FlagUncached, VMAPermission::None,
+ VMAPermission::None, attribute_mask, MemoryAttribute::None, ignore_mask);
+
+ if (result.Failed()) {
+ return result.Code();
+ }
+
+ const auto [prev_state, prev_permissions, prev_attributes] = *result;
+ const auto new_attribute = (prev_attributes & ~mask) | (mask & attribute);
+
+ const auto carve_result = CarveVMARange(address, size);
+ if (carve_result.Failed()) {
+ return carve_result.Code();
+ }
+
+ auto vma_iter = *carve_result;
+ vma_iter->second.attribute = new_attribute;
+
+ MergeAdjacent(vma_iter);
+ return RESULT_SUCCESS;
+}
+
+ResultCode VMManager::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
+ const auto vma = FindVMA(src_addr);
+
+ ASSERT_MSG(vma != 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.
+ const auto vma_offset = src_addr - vma->first;
+ ASSERT_MSG(vma_offset + size <= vma->second.size,
+ "Shared memory exceeds bounds of mapped block");
+
+ const std::shared_ptr<std::vector<u8>>& backing_block = vma->second.backing_block;
+ const std::size_t backing_block_offset = vma->second.offset + vma_offset;
+
+ CASCADE_RESULT(auto new_vma,
+ MapMemoryBlock(dst_addr, backing_block, backing_block_offset, size, state));
+ // Protect mirror with permissions from old region
+ Reprotect(new_vma, vma->second.permissions);
+ // Remove permissions from old region
+ Reprotect(vma, VMAPermission::None);
+
+ return RESULT_SUCCESS;
+}
+
void VMManager::RefreshMemoryBlockMappings(const std::vector<u8>* block) {
// If this ever proves to have a noticeable performance impact, allow users of the function to
// specify a specific range of addresses to limit the scan to.
@@ -262,7 +394,7 @@ void VMManager::LogLayout() const {
(u8)vma.permissions & (u8)VMAPermission::Read ? 'R' : '-',
(u8)vma.permissions & (u8)VMAPermission::Write ? 'W' : '-',
(u8)vma.permissions & (u8)VMAPermission::Execute ? 'X' : '-',
- GetMemoryStateName(vma.meminfo_state));
+ GetMemoryStateName(vma.state));
}
}
@@ -489,14 +621,73 @@ void VMManager::ClearPageTable() {
Memory::PageType::Unmapped);
}
+VMManager::CheckResults VMManager::CheckRangeState(VAddr address, u64 size, MemoryState state_mask,
+ MemoryState state, VMAPermission permission_mask,
+ VMAPermission permissions,
+ MemoryAttribute attribute_mask,
+ MemoryAttribute attribute,
+ MemoryAttribute ignore_mask) const {
+ auto iter = FindVMA(address);
+
+ // If we don't have a valid VMA handle at this point, then it means this is
+ // being called with an address outside of the address space, which is definitely
+ // indicative of a bug, as this function only operates on mapped memory regions.
+ DEBUG_ASSERT(IsValidHandle(iter));
+
+ const VAddr end_address = address + size - 1;
+ const MemoryAttribute initial_attributes = iter->second.attribute;
+ const VMAPermission initial_permissions = iter->second.permissions;
+ const MemoryState initial_state = iter->second.state;
+
+ while (true) {
+ // The iterator should be valid throughout the traversal. Hitting the end of
+ // the mapped VMA regions is unquestionably indicative of a bug.
+ DEBUG_ASSERT(IsValidHandle(iter));
+
+ const auto& vma = iter->second;
+
+ if (vma.state != initial_state) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if ((vma.state & state_mask) != state) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (vma.permissions != initial_permissions) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if ((vma.permissions & permission_mask) != permissions) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if ((vma.attribute | ignore_mask) != (initial_attributes | ignore_mask)) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if ((vma.attribute & attribute_mask) != attribute) {
+ return ERR_INVALID_ADDRESS_STATE;
+ }
+
+ if (end_address <= vma.EndAddress()) {
+ break;
+ }
+
+ ++iter;
+ }
+
+ return MakeResult(
+ std::make_tuple(initial_state, initial_permissions, initial_attributes & ~ignore_mask));
+}
+
u64 VMManager::GetTotalMemoryUsage() const {
LOG_WARNING(Kernel, "(STUBBED) called");
return 0xF8000000;
}
u64 VMManager::GetTotalHeapUsage() const {
- LOG_WARNING(Kernel, "(STUBBED) called");
- return 0x0;
+ return heap_used;
}
VAddr VMManager::GetAddressSpaceBaseAddress() const {
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index 2447cbb8f..6091533bc 100644
--- a/src/core/hle/kernel/vm_manager.h
+++ b/src/core/hle/kernel/vm_manager.h
@@ -6,6 +6,7 @@
#include <map>
#include <memory>
+#include <tuple>
#include <vector>
#include "common/common_types.h"
#include "core/hle/result.h"
@@ -43,26 +44,211 @@ enum class VMAPermission : u8 {
ReadWriteExecute = Read | Write | Execute,
};
-/// Set of values returned in MemoryInfo.state by svcQueryMemory.
+constexpr VMAPermission operator|(VMAPermission lhs, VMAPermission rhs) {
+ return static_cast<VMAPermission>(u32(lhs) | u32(rhs));
+}
+
+constexpr VMAPermission operator&(VMAPermission lhs, VMAPermission rhs) {
+ return static_cast<VMAPermission>(u32(lhs) & u32(rhs));
+}
+
+constexpr VMAPermission operator^(VMAPermission lhs, VMAPermission rhs) {
+ return static_cast<VMAPermission>(u32(lhs) ^ u32(rhs));
+}
+
+constexpr VMAPermission operator~(VMAPermission permission) {
+ return static_cast<VMAPermission>(~u32(permission));
+}
+
+constexpr VMAPermission& operator|=(VMAPermission& lhs, VMAPermission rhs) {
+ lhs = lhs | rhs;
+ return lhs;
+}
+
+constexpr VMAPermission& operator&=(VMAPermission& lhs, VMAPermission rhs) {
+ lhs = lhs & rhs;
+ return lhs;
+}
+
+constexpr VMAPermission& operator^=(VMAPermission& lhs, VMAPermission rhs) {
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
+/// Attribute flags that can be applied to a VMA
+enum class MemoryAttribute : u32 {
+ Mask = 0xFF,
+
+ /// No particular qualities
+ None = 0,
+ /// Memory locked/borrowed for use. e.g. This would be used by transfer memory.
+ Locked = 1,
+ /// Memory locked for use by IPC-related internals.
+ LockedForIPC = 2,
+ /// Mapped as part of the device address space.
+ DeviceMapped = 4,
+ /// Uncached memory
+ Uncached = 8,
+};
+
+constexpr MemoryAttribute operator|(MemoryAttribute lhs, MemoryAttribute rhs) {
+ return static_cast<MemoryAttribute>(u32(lhs) | u32(rhs));
+}
+
+constexpr MemoryAttribute operator&(MemoryAttribute lhs, MemoryAttribute rhs) {
+ return static_cast<MemoryAttribute>(u32(lhs) & u32(rhs));
+}
+
+constexpr MemoryAttribute operator^(MemoryAttribute lhs, MemoryAttribute rhs) {
+ return static_cast<MemoryAttribute>(u32(lhs) ^ u32(rhs));
+}
+
+constexpr MemoryAttribute operator~(MemoryAttribute attribute) {
+ return static_cast<MemoryAttribute>(~u32(attribute));
+}
+
+constexpr MemoryAttribute& operator|=(MemoryAttribute& lhs, MemoryAttribute rhs) {
+ lhs = lhs | rhs;
+ return lhs;
+}
+
+constexpr MemoryAttribute& operator&=(MemoryAttribute& lhs, MemoryAttribute rhs) {
+ lhs = lhs & rhs;
+ return lhs;
+}
+
+constexpr MemoryAttribute& operator^=(MemoryAttribute& lhs, MemoryAttribute rhs) {
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
+constexpr u32 ToSvcMemoryAttribute(MemoryAttribute attribute) {
+ return static_cast<u32>(attribute & MemoryAttribute::Mask);
+}
+
+// clang-format off
+/// Represents memory states and any relevant flags, as used by the kernel.
+/// svcQueryMemory interprets these by masking away all but the first eight
+/// bits when storing memory state into a MemoryInfo instance.
enum class MemoryState : u32 {
- Unmapped = 0x0,
- Io = 0x1,
- Normal = 0x2,
- CodeStatic = 0x3,
- CodeMutable = 0x4,
- Heap = 0x5,
- Shared = 0x6,
- ModuleCodeStatic = 0x8,
- ModuleCodeMutable = 0x9,
- IpcBuffer0 = 0xA,
- Mapped = 0xB,
- ThreadLocal = 0xC,
- TransferMemoryIsolated = 0xD,
- TransferMemory = 0xE,
- ProcessMemory = 0xF,
- IpcBuffer1 = 0x11,
- IpcBuffer3 = 0x12,
- KernelStack = 0x13,
+ Mask = 0xFF,
+ FlagProtect = 1U << 8,
+ FlagDebug = 1U << 9,
+ FlagIPC0 = 1U << 10,
+ FlagIPC3 = 1U << 11,
+ FlagIPC1 = 1U << 12,
+ FlagMapped = 1U << 13,
+ FlagCode = 1U << 14,
+ FlagAlias = 1U << 15,
+ FlagModule = 1U << 16,
+ FlagTransfer = 1U << 17,
+ FlagQueryPhysicalAddressAllowed = 1U << 18,
+ FlagSharedDevice = 1U << 19,
+ FlagSharedDeviceAligned = 1U << 20,
+ FlagIPCBuffer = 1U << 21,
+ FlagMemoryPoolAllocated = 1U << 22,
+ FlagMapProcess = 1U << 23,
+ FlagUncached = 1U << 24,
+ FlagCodeMemory = 1U << 25,
+
+ // Convenience flag sets to reduce repetition
+ IPCFlags = FlagIPC0 | FlagIPC3 | FlagIPC1,
+
+ CodeFlags = FlagDebug | IPCFlags | FlagMapped | FlagCode | FlagQueryPhysicalAddressAllowed |
+ FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
+
+ DataFlags = FlagProtect | IPCFlags | FlagMapped | FlagAlias | FlagTransfer |
+ FlagQueryPhysicalAddressAllowed | FlagSharedDevice | FlagSharedDeviceAligned |
+ FlagMemoryPoolAllocated | FlagIPCBuffer | FlagUncached,
+
+ Unmapped = 0x00,
+ Io = 0x01 | FlagMapped,
+ Normal = 0x02 | FlagMapped | FlagQueryPhysicalAddressAllowed,
+ CodeStatic = 0x03 | CodeFlags | FlagMapProcess,
+ CodeMutable = 0x04 | CodeFlags | FlagMapProcess | FlagCodeMemory,
+ Heap = 0x05 | DataFlags | FlagCodeMemory,
+ Shared = 0x06 | FlagMapped | FlagMemoryPoolAllocated,
+ ModuleCodeStatic = 0x08 | CodeFlags | FlagModule | FlagMapProcess,
+ ModuleCodeMutable = 0x09 | DataFlags | FlagModule | FlagMapProcess | FlagCodeMemory,
+
+ IpcBuffer0 = 0x0A | FlagMapped | FlagQueryPhysicalAddressAllowed | FlagMemoryPoolAllocated |
+ IPCFlags | FlagSharedDevice | FlagSharedDeviceAligned,
+
+ Stack = 0x0B | FlagMapped | IPCFlags | FlagQueryPhysicalAddressAllowed |
+ FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
+
+ ThreadLocal = 0x0C | FlagMapped | FlagMemoryPoolAllocated,
+
+ TransferMemoryIsolated = 0x0D | IPCFlags | FlagMapped | FlagQueryPhysicalAddressAllowed |
+ FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated |
+ FlagUncached,
+
+ TransferMemory = 0x0E | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed |
+ FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
+
+ ProcessMemory = 0x0F | FlagIPC3 | FlagIPC1 | FlagMapped | FlagMemoryPoolAllocated,
+
+ // Used to signify an inaccessible or invalid memory region with memory queries
+ Inaccessible = 0x10,
+
+ IpcBuffer1 = 0x11 | FlagIPC3 | FlagIPC1 | FlagMapped | FlagQueryPhysicalAddressAllowed |
+ FlagSharedDevice | FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
+
+ IpcBuffer3 = 0x12 | FlagIPC3 | FlagMapped | FlagQueryPhysicalAddressAllowed |
+ FlagSharedDeviceAligned | FlagMemoryPoolAllocated,
+
+ KernelStack = 0x13 | FlagMapped,
+};
+// clang-format on
+
+constexpr MemoryState operator|(MemoryState lhs, MemoryState rhs) {
+ return static_cast<MemoryState>(u32(lhs) | u32(rhs));
+}
+
+constexpr MemoryState operator&(MemoryState lhs, MemoryState rhs) {
+ return static_cast<MemoryState>(u32(lhs) & u32(rhs));
+}
+
+constexpr MemoryState operator^(MemoryState lhs, MemoryState rhs) {
+ return static_cast<MemoryState>(u32(lhs) ^ u32(rhs));
+}
+
+constexpr MemoryState operator~(MemoryState lhs) {
+ return static_cast<MemoryState>(~u32(lhs));
+}
+
+constexpr MemoryState& operator|=(MemoryState& lhs, MemoryState rhs) {
+ lhs = lhs | rhs;
+ return lhs;
+}
+
+constexpr MemoryState& operator&=(MemoryState& lhs, MemoryState rhs) {
+ lhs = lhs & rhs;
+ return lhs;
+}
+
+constexpr MemoryState& operator^=(MemoryState& lhs, MemoryState rhs) {
+ lhs = lhs ^ rhs;
+ return lhs;
+}
+
+constexpr u32 ToSvcMemoryState(MemoryState state) {
+ return static_cast<u32>(state & MemoryState::Mask);
+}
+
+struct MemoryInfo {
+ u64 base_address;
+ u64 size;
+ u32 state;
+ u32 attributes;
+ u32 permission;
+ u32 ipc_ref_count;
+ u32 device_ref_count;
+};
+static_assert(sizeof(MemoryInfo) == 0x28, "MemoryInfo has incorrect size.");
+
+struct PageInfo {
+ u32 flags;
};
/**
@@ -71,6 +257,16 @@ enum class MemoryState : u32 {
* also backed by a single host memory allocation.
*/
struct VirtualMemoryArea {
+ /// Gets the starting (base) address of this VMA.
+ VAddr StartAddress() const {
+ return base;
+ }
+
+ /// Gets the ending address of this VMA.
+ VAddr EndAddress() const {
+ return base + size - 1;
+ }
+
/// Virtual base address of the region.
VAddr base = 0;
/// Size of the region.
@@ -78,8 +274,8 @@ struct VirtualMemoryArea {
VMAType type = VMAType::Free;
VMAPermission permissions = VMAPermission::None;
- /// Tag returned by svcQueryMemory. Not otherwise used.
- MemoryState meminfo_state = MemoryState::Unmapped;
+ MemoryState state = MemoryState::Unmapped;
+ MemoryAttribute attribute = MemoryAttribute::None;
// Settings for type = AllocatedMemoryBlock
/// Memory block backing this VMA.
@@ -113,16 +309,10 @@ struct VirtualMemoryArea {
* - http://duartes.org/gustavo/blog/post/page-cache-the-affair-between-memory-and-files/
*/
class VMManager final {
+ using VMAMap = std::map<VAddr, VirtualMemoryArea>;
+
public:
- /**
- * A map covering the entirety of the managed address space, keyed by the `base` field of each
- * VMA. It must always be modified by splitting or merging VMAs, so that the invariant
- * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be
- * merged when possible so that no two similar and adjacent regions exist that have not been
- * merged.
- */
- std::map<VAddr, VirtualMemoryArea> vma_map;
- using VMAHandle = decltype(vma_map)::const_iterator;
+ using VMAHandle = VMAMap::const_iterator;
VMManager();
~VMManager();
@@ -133,6 +323,9 @@ public:
/// Finds the VMA in which the given address is included in, or `vma_map.end()`.
VMAHandle FindVMA(VAddr target) const;
+ /// Indicates whether or not the given handle is within the VMA map.
+ bool IsValidHandle(VMAHandle handle) const;
+
// TODO(yuriks): Should these functions actually return the handle?
/**
@@ -186,6 +379,32 @@ public:
/// Changes the permissions of a range of addresses, splitting VMAs as necessary.
ResultCode ReprotectRange(VAddr target, u64 size, VMAPermission new_perms);
+ ResultVal<VAddr> HeapAllocate(VAddr target, u64 size, VMAPermission perms);
+ ResultCode HeapFree(VAddr target, u64 size);
+
+ ResultCode MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state);
+
+ /// Queries the memory manager for information about the given address.
+ ///
+ /// @param address The address to query the memory manager about for information.
+ ///
+ /// @return A MemoryInfo instance containing information about the given address.
+ ///
+ MemoryInfo QueryMemory(VAddr address) const;
+
+ /// Sets an attribute across the given address range.
+ ///
+ /// @param address The starting address
+ /// @param size The size of the range to set the attribute on.
+ /// @param mask The attribute mask
+ /// @param attribute The attribute to set across the given address range
+ ///
+ /// @returns RESULT_SUCCESS if successful
+ /// @returns ERR_INVALID_ADDRESS_STATE if the attribute could not be set.
+ ///
+ ResultCode SetMemoryAttribute(VAddr address, u64 size, MemoryAttribute mask,
+ MemoryAttribute attribute);
+
/**
* Scans all VMAs and updates the page table range of any that use the given vector as backing
* memory. This should be called after any operation that causes reallocation of the vector.
@@ -275,7 +494,7 @@ public:
Memory::PageTable page_table;
private:
- using VMAIter = decltype(vma_map)::iterator;
+ using VMAIter = VMAMap::iterator;
/// Converts a VMAHandle to a mutable VMAIter.
VMAIter StripIterConstness(const VMAHandle& iter);
@@ -322,6 +541,44 @@ private:
/// Clears out the page table
void ClearPageTable();
+ using CheckResults = ResultVal<std::tuple<MemoryState, VMAPermission, MemoryAttribute>>;
+
+ /// Checks if an address range adheres to the specified states provided.
+ ///
+ /// @param address The starting address of the address range.
+ /// @param size The size of the address range.
+ /// @param state_mask The memory state mask.
+ /// @param state The state to compare the individual VMA states against,
+ /// which is done in the form of: (vma.state & state_mask) != state.
+ /// @param permission_mask The memory permissions mask.
+ /// @param permissions The permission to compare the individual VMA permissions against,
+ /// which is done in the form of:
+ /// (vma.permission & permission_mask) != permission.
+ /// @param attribute_mask The memory attribute mask.
+ /// @param attribute The memory attributes to compare the individual VMA attributes
+ /// against, which is done in the form of:
+ /// (vma.attributes & attribute_mask) != attribute.
+ /// @param ignore_mask The memory attributes to ignore during the check.
+ ///
+ /// @returns If successful, returns a tuple containing the memory attributes
+ /// (with ignored bits specified by ignore_mask unset), memory permissions, and
+ /// memory state across the memory range.
+ /// @returns If not successful, returns ERR_INVALID_ADDRESS_STATE.
+ ///
+ CheckResults CheckRangeState(VAddr address, u64 size, MemoryState state_mask, MemoryState state,
+ VMAPermission permission_mask, VMAPermission permissions,
+ MemoryAttribute attribute_mask, MemoryAttribute attribute,
+ MemoryAttribute ignore_mask) const;
+
+ /**
+ * A map covering the entirety of the managed address space, keyed by the `base` field of each
+ * VMA. It must always be modified by splitting or merging VMAs, so that the invariant
+ * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be
+ * merged when possible so that no two similar and adjacent regions exist that have not been
+ * merged.
+ */
+ VMAMap vma_map;
+
u32 address_space_width = 0;
VAddr address_space_base = 0;
VAddr address_space_end = 0;
@@ -343,5 +600,15 @@ private:
VAddr tls_io_region_base = 0;
VAddr tls_io_region_end = 0;
+
+ // Memory used to back the allocations in the regular heap. A single vector is used to cover
+ // the entire virtual address space extents that bound the allocations, including any holes.
+ // This makes deallocation and reallocation of holes fast and keeps process memory contiguous
+ // in the emulator address space, allowing Memory::GetPointer to be reasonably safe.
+ std::shared_ptr<std::vector<u8>> heap_memory;
+ // The left/right bounds of the address space covered by heap_memory.
+ VAddr heap_start = 0;
+ VAddr heap_end = 0;
+ u64 heap_used = 0;
};
} // namespace Kernel
diff --git a/src/core/hle/kernel/wait_object.cpp b/src/core/hle/kernel/wait_object.cpp
index 530ee6af7..90580ed93 100644
--- a/src/core/hle/kernel/wait_object.cpp
+++ b/src/core/hle/kernel/wait_object.cpp
@@ -4,11 +4,11 @@
#include <algorithm>
#include "common/assert.h"
+#include "common/common_types.h"
#include "common/logging/log.h"
#include "core/hle/kernel/object.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/thread.h"
-#include "core/hle/kernel/timer.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/wait_object.h b/src/core/hle/kernel/wait_object.h
index f4367ee28..d70b67893 100644
--- a/src/core/hle/kernel/wait_object.h
+++ b/src/core/hle/kernel/wait_object.h
@@ -6,7 +6,6 @@
#include <vector>
#include <boost/smart_ptr/intrusive_ptr.hpp>
-#include "common/common_types.h"
#include "core/hle/kernel/object.h"
namespace Kernel {
diff --git a/src/core/hle/kernel/writable_event.cpp b/src/core/hle/kernel/writable_event.cpp
new file mode 100644
index 000000000..a58ea6ec8
--- /dev/null
+++ b/src/core/hle/kernel/writable_event.cpp
@@ -0,0 +1,52 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include "common/assert.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
+
+namespace Kernel {
+
+WritableEvent::WritableEvent(KernelCore& kernel) : Object{kernel} {}
+WritableEvent::~WritableEvent() = default;
+
+EventPair WritableEvent::CreateEventPair(KernelCore& kernel, ResetType reset_type,
+ std::string name) {
+ SharedPtr<WritableEvent> writable_event(new WritableEvent(kernel));
+ SharedPtr<ReadableEvent> readable_event(new ReadableEvent(kernel));
+
+ writable_event->name = name + ":Writable";
+ writable_event->readable = readable_event;
+ readable_event->name = name + ":Readable";
+ readable_event->signaled = false;
+ readable_event->reset_type = reset_type;
+
+ return {std::move(readable_event), std::move(writable_event)};
+}
+
+SharedPtr<ReadableEvent> WritableEvent::GetReadableEvent() const {
+ return readable;
+}
+
+ResetType WritableEvent::GetResetType() const {
+ return readable->reset_type;
+}
+
+void WritableEvent::Signal() {
+ readable->Signal();
+}
+
+void WritableEvent::Clear() {
+ readable->Clear();
+}
+
+bool WritableEvent::IsSignaled() const {
+ return readable->signaled;
+}
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/writable_event.h b/src/core/hle/kernel/writable_event.h
new file mode 100644
index 000000000..c9068dd3d
--- /dev/null
+++ b/src/core/hle/kernel/writable_event.h
@@ -0,0 +1,61 @@
+// Copyright 2014 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/kernel/object.h"
+
+namespace Kernel {
+
+class KernelCore;
+class ReadableEvent;
+class WritableEvent;
+
+struct EventPair {
+ SharedPtr<ReadableEvent> readable;
+ SharedPtr<WritableEvent> writable;
+};
+
+class WritableEvent final : public Object {
+public:
+ ~WritableEvent() override;
+
+ /**
+ * Creates an event
+ * @param kernel The kernel instance to create this event under.
+ * @param reset_type ResetType describing how to create event
+ * @param name Optional name of event
+ */
+ static EventPair CreateEventPair(KernelCore& kernel, ResetType reset_type,
+ std::string name = "Unknown");
+
+ std::string GetTypeName() const override {
+ return "WritableEvent";
+ }
+ std::string GetName() const override {
+ return name;
+ }
+
+ static const HandleType HANDLE_TYPE = HandleType::WritableEvent;
+ HandleType GetHandleType() const override {
+ return HANDLE_TYPE;
+ }
+
+ SharedPtr<ReadableEvent> GetReadableEvent() const;
+
+ ResetType GetResetType() const;
+
+ void Signal();
+ void Clear();
+ bool IsSignaled() const;
+
+private:
+ explicit WritableEvent(KernelCore& kernel);
+
+ SharedPtr<ReadableEvent> readable;
+
+ std::string name; ///< Name of event (optional)
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/result.h b/src/core/hle/result.h
index c6b18cfba..bfb77cc31 100644
--- a/src/core/hle/result.h
+++ b/src/core/hle/result.h
@@ -19,8 +19,6 @@
enum class ErrorDescription : u32 {
Success = 0,
RemoteProcessDead = 301,
- InvalidOffset = 6061,
- InvalidLength = 6062,
};
/**
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index c6437a671..1f8ed265e 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -21,17 +21,6 @@
namespace Service::Account {
-// TODO: RE this structure
-struct UserData {
- INSERT_PADDING_WORDS(1);
- u32 icon_id;
- u8 bg_color_id;
- INSERT_PADDING_BYTES(0x7);
- INSERT_PADDING_BYTES(0x10);
- INSERT_PADDING_BYTES(0x60);
-};
-static_assert(sizeof(UserData) == 0x80, "UserData structure has incorrect size");
-
// Smallest JPEG https://github.com/mathiasbynens/small/blob/master/jpeg.jpg
// used as a backup should the one on disk not exist
constexpr u32 backup_jpeg_size = 107;
@@ -72,9 +61,11 @@ private:
void Get(Kernel::HLERequestContext& ctx) {
LOG_INFO(Service_ACC, "called user_id={}", user_id.Format());
ProfileBase profile_base{};
- std::array<u8, MAX_DATA> data{};
+ ProfileData data{};
if (profile_manager.GetProfileBaseAndData(user_id, profile_base, data)) {
- ctx.WriteBuffer(data);
+ std::array<u8, sizeof(ProfileData)> raw_data;
+ std::memcpy(raw_data.data(), &data, sizeof(ProfileData));
+ ctx.WriteBuffer(raw_data);
IPC::ResponseBuilder rb{ctx, 16};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(profile_base);
@@ -216,10 +207,11 @@ void Module::Interface::GetLastOpenedUser(Kernel::HLERequestContext& ctx) {
void Module::Interface::GetProfile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
UUID user_id = rp.PopRaw<UUID>();
+ LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IProfile>(user_id, *profile_manager);
- LOG_DEBUG(Service_ACC, "called user_id={}", user_id.Format());
}
void Module::Interface::IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx) {
@@ -236,10 +228,34 @@ void Module::Interface::InitializeApplicationInfo(Kernel::HLERequestContext& ctx
}
void Module::Interface::GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ACC, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IManagerForApplication>();
+}
+
+void Module::Interface::TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_ACC, "called");
+ // A u8 is passed into this function which we can safely ignore. It's to determine if we have
+ // access to use the network or not by the looks of it
+ IPC::ResponseBuilder rb{ctx, 6};
+ if (profile_manager->GetUserCount() != 1) {
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u128>(INVALID_UUID);
+ return;
+ }
+
+ const auto user_list = profile_manager->GetAllUsers();
+ if (std::all_of(user_list.begin(), user_list.end(),
+ [](const auto& user) { return user.uuid == INVALID_UUID; })) {
+ rb.Push(ResultCode(-1)); // TODO(ogniK): Find the correct error code
+ rb.PushRaw<u128>(INVALID_UUID);
+ return;
+ }
+
+ // Select the first user we have
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u128>(profile_manager->GetUser(0)->uuid);
}
Module::Interface::Interface(std::shared_ptr<Module> module,
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index c7ed74351..89b2104fa 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -27,6 +27,7 @@ public:
void InitializeApplicationInfo(Kernel::HLERequestContext& ctx);
void GetBaasAccountManagerForApplication(Kernel::HLERequestContext& ctx);
void IsUserRegistrationRequestPermitted(Kernel::HLERequestContext& ctx);
+ void TrySelectUserWithoutInteraction(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> module;
diff --git a/src/core/hle/service/acc/acc_su.cpp b/src/core/hle/service/acc/acc_su.cpp
index ad455c3a7..5e2030355 100644
--- a/src/core/hle/service/acc/acc_su.cpp
+++ b/src/core/hle/service/acc/acc_su.cpp
@@ -17,7 +17,7 @@ ACC_SU::ACC_SU(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{5, &ACC_SU::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, &ACC_SU::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
- {51, nullptr, "TrySelectUserWithoutInteraction"},
+ {51, &ACC_SU::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index 72d4adf35..a4d705b45 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -17,7 +17,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{5, &ACC_U0::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, &ACC_U0::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
- {51, nullptr, "TrySelectUserWithoutInteraction"},
+ {51, &ACC_U0::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, &ACC_U0::InitializeApplicationInfo, "InitializeApplicationInfo"},
{101, &ACC_U0::GetBaasAccountManagerForApplication, "GetBaasAccountManagerForApplication"},
diff --git a/src/core/hle/service/acc/acc_u1.cpp b/src/core/hle/service/acc/acc_u1.cpp
index d480f08e5..8fffc93b5 100644
--- a/src/core/hle/service/acc/acc_u1.cpp
+++ b/src/core/hle/service/acc/acc_u1.cpp
@@ -17,7 +17,7 @@ ACC_U1::ACC_U1(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{5, &ACC_U1::GetProfile, "GetProfile"},
{6, nullptr, "GetProfileDigest"},
{50, &ACC_U1::IsUserRegistrationRequestPermitted, "IsUserRegistrationRequestPermitted"},
- {51, nullptr, "TrySelectUserWithoutInteraction"},
+ {51, &ACC_U1::TrySelectUserWithoutInteraction, "TrySelectUserWithoutInteraction"},
{60, nullptr, "ListOpenContextStoredUsers"},
{100, nullptr, "GetUserRegistrationNotifier"},
{101, nullptr, "GetUserStateChangeNotifier"},
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp
index 3cac1b4ff..1316d0b07 100644
--- a/src/core/hle/service/acc/profile_manager.cpp
+++ b/src/core/hle/service/acc/profile_manager.cpp
@@ -2,8 +2,11 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cstring>
#include <random>
+#include <fmt/format.h>
+
#include "common/file_util.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/settings.h"
@@ -15,7 +18,7 @@ struct UserRaw {
UUID uuid2;
u64 timestamp;
ProfileUsername username;
- INSERT_PADDING_BYTES(0x80);
+ ProfileData extra_data;
};
static_assert(sizeof(UserRaw) == 0xC8, "UserRaw has incorrect size.");
@@ -39,6 +42,19 @@ UUID UUID::Generate() {
return UUID{distribution(gen), distribution(gen)};
}
+std::string UUID::Format() const {
+ return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
+}
+
+std::string UUID::FormatSwitch() const {
+ std::array<u8, 16> s{};
+ std::memcpy(s.data(), uuid.data(), sizeof(u128));
+ return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
+ ":02x}{:02x}{:02x}{:02x}{:02x}",
+ s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
+ s[12], s[13], s[14], s[15]);
+}
+
ProfileManager::ProfileManager() {
ParseUserSaveFile();
@@ -195,7 +211,7 @@ std::size_t ProfileManager::GetOpenUserCount() const {
/// Checks if a user id exists in our profile manager
bool ProfileManager::UserExists(UUID uuid) const {
- return GetUserIndex(uuid) != std::nullopt;
+ return GetUserIndex(uuid).has_value();
}
bool ProfileManager::UserExistsIndex(std::size_t index) const {
@@ -325,11 +341,12 @@ void ProfileManager::ParseUserSaveFile() {
return;
}
- for (std::size_t i = 0; i < MAX_USERS; ++i) {
- const auto& user = data.users[i];
+ for (const auto& user : data.users) {
+ if (user.uuid == UUID(INVALID_UUID)) {
+ continue;
+ }
- if (user.uuid != UUID(INVALID_UUID))
- AddUser({user.uuid, user.username, user.timestamp, {}, false});
+ AddUser({user.uuid, user.username, user.timestamp, user.extra_data, false});
}
std::stable_partition(profiles.begin(), profiles.end(),
@@ -344,6 +361,7 @@ void ProfileManager::WriteUserSaveFile() {
raw.users[i].uuid2 = profiles[i].user_uuid;
raw.users[i].uuid = profiles[i].user_uuid;
raw.users[i].timestamp = profiles[i].creation_time;
+ raw.users[i].extra_data = profiles[i].data;
}
const auto raw_path =
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h
index 1cd2e51b2..c4ce2e0b3 100644
--- a/src/core/hle/service/acc/profile_manager.h
+++ b/src/core/hle/service/acc/profile_manager.h
@@ -13,7 +13,6 @@
namespace Service::Account {
constexpr std::size_t MAX_USERS = 8;
-constexpr std::size_t MAX_DATA = 128;
constexpr u128 INVALID_UUID{{0, 0}};
struct UUID {
@@ -42,25 +41,28 @@ struct UUID {
void Invalidate() {
uuid = INVALID_UUID;
}
- std::string Format() const {
- return fmt::format("0x{:016X}{:016X}", uuid[1], uuid[0]);
- }
- std::string FormatSwitch() const {
- std::array<u8, 16> s{};
- std::memcpy(s.data(), uuid.data(), sizeof(u128));
- return fmt::format("{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{"
- ":02x}{:02x}{:02x}{:02x}{:02x}",
- s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7], s[8], s[9], s[10], s[11],
- s[12], s[13], s[14], s[15]);
- }
+ std::string Format() const;
+ std::string FormatSwitch() const;
};
static_assert(sizeof(UUID) == 16, "UUID is an invalid size!");
-using ProfileUsername = std::array<u8, 0x20>;
-using ProfileData = std::array<u8, MAX_DATA>;
+constexpr std::size_t profile_username_size = 32;
+using ProfileUsername = std::array<u8, profile_username_size>;
using UserIDArray = std::array<UUID, MAX_USERS>;
+/// Contains extra data related to a user.
+/// TODO: RE this structure
+struct ProfileData {
+ INSERT_PADDING_WORDS(1);
+ u32 icon_id;
+ u8 bg_color_id;
+ INSERT_PADDING_BYTES(0x7);
+ INSERT_PADDING_BYTES(0x10);
+ INSERT_PADDING_BYTES(0x60);
+};
+static_assert(sizeof(ProfileData) == 0x80, "ProfileData structure has incorrect size");
+
/// This holds general information about a users profile. This is where we store all the information
/// based on a specific user
struct ProfileInfo {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 59aafd616..d13ce4dca 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -6,14 +6,23 @@
#include <cinttypes>
#include <cstring>
#include <stack>
+#include "audio_core/audio_renderer.h"
#include "core/core.h"
+#include "core/file_sys/savedata_factory.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
+#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/am/applets/profile_select.h"
+#include "core/hle/service/am/applets/software_keyboard.h"
+#include "core/hle/service/am/applets/stub_applet.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
@@ -28,6 +37,14 @@
namespace Service::AM {
+constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
+constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
+
+enum class AppletId : u32 {
+ ProfileSelect = 0x10,
+ SoftwareKeyboard = 0x11,
+};
+
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
struct LaunchParameters {
@@ -57,10 +74,13 @@ IWindowController::IWindowController() : ServiceFramework("IWindowController") {
IWindowController::~IWindowController() = default;
void IWindowController::GetAppletResourceUserId(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ const u64 process_id = Core::System::GetInstance().Kernel().CurrentProcess()->GetProcessID();
+
+ LOG_DEBUG(Service_AM, "called. Process ID=0x{:016X}", process_id);
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(0);
+ rb.Push<u64>(process_id);
}
void IWindowController::AcquireForegroundRights(Kernel::HLERequestContext& ctx) {
@@ -196,15 +216,16 @@ ISelfController::ISelfController(std::shared_ptr<NVFlinger::NVFlinger> nvflinger
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- launchable_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "ISelfController:LaunchableEvent");
+ launchable_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ "ISelfController:LaunchableEvent");
}
ISelfController::~ISelfController() = default;
void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
- // Takes 3 input u8s with each field located immediately after the previous u8, these are
- // bool flags. No output.
+ // Takes 3 input u8s with each field located immediately after the previous
+ // u8, these are bool flags. No output.
+ LOG_WARNING(Service_AM, "(STUBBED) called");
IPC::RequestParser rp{ctx};
@@ -217,128 +238,172 @@ void ISelfController::SetFocusHandlingMode(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetRestartMessageEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetPerformanceModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
bool flag = rp.Pop<bool>();
+ LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
}
void ISelfController::SetScreenShotPermission(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetOperationModeChangedNotification(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
bool flag = rp.Pop<bool>();
+ LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called flag={}", flag);
}
void ISelfController::SetOutOfFocusSuspendingEnabled(Kernel::HLERequestContext& ctx) {
- // Takes 3 input u8s with each field located immediately after the previous u8, these are
- // bool flags. No output.
+ // Takes 3 input u8s with each field located immediately after the previous
+ // u8, these are bool flags. No output.
IPC::RequestParser rp{ctx};
bool enabled = rp.Pop<bool>();
+ LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called enabled={}", enabled);
}
void ISelfController::LockExit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::UnlockExit(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::GetLibraryAppletLaunchableEvent(Kernel::HLERequestContext& ctx) {
- launchable_event->Signal();
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ launchable_event.writable->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(launchable_event);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ rb.PushCopyObjects(launchable_event.readable);
}
void ISelfController::SetScreenShotImageOrientation(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::CreateManagedDisplayLayer(Kernel::HLERequestContext& ctx) {
- // TODO(Subv): Find out how AM determines the display to use, for now just create the layer
- // in the Default display.
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+ // TODO(Subv): Find out how AM determines the display to use, for now just
+ // create the layer in the Default display.
u64 display_id = nvflinger->OpenDisplay("Default");
u64 layer_id = nvflinger->CreateLayer(display_id);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(layer_id);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetHandlesRequestToDisplay(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::SetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
idle_time_detection_extension = rp.Pop<u32>();
+ LOG_WARNING(Service_AM, "(STUBBED) called idle_time_detection_extension={}",
+ idle_time_detection_extension);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ISelfController::GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(idle_time_detection_extension);
+}
- LOG_WARNING(Service_AM, "(STUBBED) called");
+AppletMessageQueue::AppletMessageQueue() {
+ auto& kernel = Core::System::GetInstance().Kernel();
+ on_new_message = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ "AMMessageQueue:OnMessageRecieved");
+ on_operation_mode_changed = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot, "AMMessageQueue:OperationModeChanged");
+}
+
+AppletMessageQueue::~AppletMessageQueue() = default;
+
+const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetMesssageRecieveEvent()
+ const {
+ return on_new_message.readable;
+}
+
+const Kernel::SharedPtr<Kernel::ReadableEvent>& AppletMessageQueue::GetOperationModeChangedEvent()
+ const {
+ return on_operation_mode_changed.readable;
+}
+
+void AppletMessageQueue::PushMessage(AppletMessage msg) {
+ messages.push(msg);
+ on_new_message.writable->Signal();
+}
+
+AppletMessageQueue::AppletMessage AppletMessageQueue::PopMessage() {
+ if (messages.empty()) {
+ on_new_message.writable->Clear();
+ return AppletMessage::NoMessage;
+ }
+ auto msg = messages.front();
+ messages.pop();
+ if (messages.empty()) {
+ on_new_message.writable->Clear();
+ }
+ return msg;
}
-ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter") {
+std::size_t AppletMessageQueue::GetMessageCount() const {
+ return messages.size();
+}
+
+void AppletMessageQueue::OperationModeChanged() {
+ PushMessage(AppletMessage::OperationModeChanged);
+ PushMessage(AppletMessage::PerformanceModeChanged);
+ on_operation_mode_changed.writable->Signal();
+}
+
+ICommonStateGetter::ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue)
+ : ServiceFramework("ICommonStateGetter"), msg_queue(std::move(msg_queue)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &ICommonStateGetter::GetEventHandle, "GetEventHandle"},
@@ -371,101 +436,131 @@ ICommonStateGetter::ICommonStateGetter() : ServiceFramework("ICommonStateGetter"
// clang-format on
RegisterHandlers(functions);
-
- auto& kernel = Core::System::GetInstance().Kernel();
- event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "ICommonStateGetter:Event");
}
ICommonStateGetter::~ICommonStateGetter() = default;
void ICommonStateGetter::GetBootMode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(static_cast<u8>(Service::PM::SystemBootMode::Normal)); // Normal boot mode
-
- LOG_DEBUG(Service_AM, "called");
}
void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
- event->Signal();
+ LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(event);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ rb.PushCopyObjects(msg_queue->GetMesssageRecieveEvent());
}
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(15);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(FocusState::InFocus));
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::GetDefaultDisplayResolutionChangeEvent(Kernel::HLERequestContext& ctx) {
- event->Signal();
+ LOG_DEBUG(Service_AM, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(event);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ rb.PushCopyObjects(msg_queue->GetOperationModeChangedEvent());
}
void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
+ static_cast<u32>(Settings::values.resolution_factor));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
+ static_cast<u32>(Settings::values.resolution_factor));
} else {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
+ static_cast<u32>(Settings::values.resolution_factor));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
+ static_cast<u32>(Settings::values.resolution_factor));
}
+}
- LOG_DEBUG(Service_AM, "called");
+IStorage::IStorage(std::vector<u8> buffer)
+ : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IStorage::Open, "Open"},
+ {1, nullptr, "OpenTransferStorage"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+IStorage::~IStorage() = default;
+
+const std::vector<u8>& IStorage::GetData() const {
+ return buffer;
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
const bool use_docked_mode{Settings::values.use_docked_mode};
+ LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u8>(use_docked_mode ? OperationMode::Docked : OperationMode::Handheld));
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void ICommonStateGetter::GetPerformanceMode(Kernel::HLERequestContext& ctx) {
const bool use_docked_mode{Settings::values.use_docked_mode};
+ LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(use_docked_mode ? APM::PerformanceMode::Docked
: APM::PerformanceMode::Handheld));
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
-class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
+class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
public:
- explicit IStorageAccessor(std::vector<u8> buffer)
- : ServiceFramework("IStorageAccessor"), buffer(std::move(buffer)) {
+ explicit ILibraryAppletAccessor(std::shared_ptr<Applets::Applet> applet)
+ : ServiceFramework("ILibraryAppletAccessor"), applet(std::move(applet)) {
// clang-format off
static const FunctionInfo functions[] = {
- {0, &IStorageAccessor::GetSize, "GetSize"},
- {10, &IStorageAccessor::Write, "Write"},
- {11, &IStorageAccessor::Read, "Read"},
+ {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
+ {1, &ILibraryAppletAccessor::IsCompleted, "IsCompleted"},
+ {10, &ILibraryAppletAccessor::Start, "Start"},
+ {20, nullptr, "RequestExit"},
+ {25, nullptr, "Terminate"},
+ {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
+ {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
+ {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
+ {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
+ {102, nullptr, "PushExtraStorage"},
+ {103, &ILibraryAppletAccessor::PushInteractiveInData, "PushInteractiveInData"},
+ {104, &ILibraryAppletAccessor::PopInteractiveOutData, "PopInteractiveOutData"},
+ {105, &ILibraryAppletAccessor::GetPopOutDataEvent, "GetPopOutDataEvent"},
+ {106, &ILibraryAppletAccessor::GetPopInteractiveOutDataEvent, "GetPopInteractiveOutDataEvent"},
+ {110, nullptr, "NeedsToExitProcess"},
+ {120, nullptr, "GetLibraryAppletInfo"},
+ {150, nullptr, "RequestForAppletToGetForeground"},
+ {160, nullptr, "GetIndirectLayerConsumerHandle"},
};
// clang-format on
@@ -473,158 +568,199 @@ public:
}
private:
- std::vector<u8> buffer;
+ void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
- void GetSize(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 4};
+ const auto event = applet->GetBroker().GetStateChangedEvent();
+ IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.Push(static_cast<u64>(buffer.size()));
+ rb.PushCopyObjects(event);
+ }
+ void IsCompleted(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(applet->TransactionComplete());
}
- void Write(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
+ void GetResult(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(applet->GetStatus());
+ }
- const u64 offset{rp.Pop<u64>()};
- const std::vector<u8> data{ctx.ReadBuffer()};
+ void Start(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
- ASSERT(offset + data.size() <= buffer.size());
+ ASSERT(applet != nullptr);
- std::memcpy(&buffer[offset], data.data(), data.size());
+ applet->Initialize();
+ applet->Execute();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_DEBUG(Service_AM, "called, offset={}", offset);
}
- void Read(Kernel::HLERequestContext& ctx) {
+ void PushInData(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::RequestParser rp{ctx};
+ applet->GetBroker().PushNormalDataFromGame(*rp.PopIpcInterface<IStorage>());
- const u64 offset{rp.Pop<u64>()};
- const std::size_t size{ctx.GetWriteBufferSize()};
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
- ASSERT(offset + size <= buffer.size());
+ void PopOutData(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
- ctx.WriteBuffer(buffer.data() + offset, size);
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ const auto storage = applet->GetBroker().PopNormalDataToGame();
+ if (storage == nullptr) {
+ LOG_ERROR(Service_AM,
+ "storage is a nullptr. There is no data in the current normal channel");
- LOG_DEBUG(Service_AM, "called, offset={}", offset);
+ rb.Push(ERR_NO_DATA_IN_CHANNEL);
+ return;
+ }
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IStorage>(std::move(*storage));
}
-};
-class IStorage final : public ServiceFramework<IStorage> {
-public:
- explicit IStorage(std::vector<u8> buffer)
- : ServiceFramework("IStorage"), buffer(std::move(buffer)) {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &IStorage::Open, "Open"},
- {1, nullptr, "OpenTransferStorage"},
- };
- // clang-format on
+ void PushInteractiveInData(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
- RegisterHandlers(functions);
+ IPC::RequestParser rp{ctx};
+ applet->GetBroker().PushInteractiveDataFromGame(*rp.PopIpcInterface<IStorage>());
+
+ ASSERT(applet->IsInitialized());
+ applet->ExecuteInteractive();
+ applet->Execute();
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
-private:
- std::vector<u8> buffer;
+ void PopInteractiveOutData(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
- void Open(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ const auto storage = applet->GetBroker().PopInteractiveDataToGame();
+ if (storage == nullptr) {
+ LOG_ERROR(Service_AM,
+ "storage is a nullptr. There is no data in the current interactive channel");
+
+ rb.Push(ERR_NO_DATA_IN_CHANNEL);
+ return;
+ }
+
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<AM::IStorageAccessor>(buffer);
+ rb.PushIpcInterface<IStorage>(std::move(*storage));
+ }
+ void GetPopOutDataEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(applet->GetBroker().GetNormalDataEvent());
+ }
+
+ void GetPopInteractiveOutDataEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(applet->GetBroker().GetInteractiveDataEvent());
}
+
+ std::shared_ptr<Applets::Applet> applet;
};
-class ILibraryAppletAccessor final : public ServiceFramework<ILibraryAppletAccessor> {
-public:
- explicit ILibraryAppletAccessor() : ServiceFramework("ILibraryAppletAccessor") {
- // clang-format off
+void IStorage::Open(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IStorageAccessor>(*this);
+}
+
+IStorageAccessor::IStorageAccessor(IStorage& storage)
+ : ServiceFramework("IStorageAccessor"), backing(storage) {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, &ILibraryAppletAccessor::GetAppletStateChangedEvent, "GetAppletStateChangedEvent"},
- {1, nullptr, "IsCompleted"},
- {10, &ILibraryAppletAccessor::Start, "Start"},
- {20, nullptr, "RequestExit"},
- {25, nullptr, "Terminate"},
- {30, &ILibraryAppletAccessor::GetResult, "GetResult"},
- {50, nullptr, "SetOutOfFocusApplicationSuspendingEnabled"},
- {100, &ILibraryAppletAccessor::PushInData, "PushInData"},
- {101, &ILibraryAppletAccessor::PopOutData, "PopOutData"},
- {102, nullptr, "PushExtraStorage"},
- {103, nullptr, "PushInteractiveInData"},
- {104, nullptr, "PopInteractiveOutData"},
- {105, nullptr, "GetPopOutDataEvent"},
- {106, nullptr, "GetPopInteractiveOutDataEvent"},
- {110, nullptr, "NeedsToExitProcess"},
- {120, nullptr, "GetLibraryAppletInfo"},
- {150, nullptr, "RequestForAppletToGetForeground"},
- {160, nullptr, "GetIndirectLayerConsumerHandle"},
+ {0, &IStorageAccessor::GetSize, "GetSize"},
+ {10, &IStorageAccessor::Write, "Write"},
+ {11, &IStorageAccessor::Read, "Read"},
};
- // clang-format on
+ // clang-format on
- RegisterHandlers(functions);
+ RegisterHandlers(functions);
+}
- auto& kernel = Core::System::GetInstance().Kernel();
- state_changed_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
- "ILibraryAppletAccessor:StateChangedEvent");
- }
+IStorageAccessor::~IStorageAccessor() = default;
-private:
- void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
- state_changed_event->Signal();
+void IStorageAccessor::GetSize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(state_changed_event);
+ IPC::ResponseBuilder rb{ctx, 4};
- LOG_WARNING(Service_AM, "(STUBBED) called");
- }
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(static_cast<u64>(backing.buffer.size()));
+}
- void GetResult(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
- LOG_WARNING(Service_AM, "(STUBBED) called");
- }
+ const u64 offset{rp.Pop<u64>()};
+ LOG_DEBUG(Service_AM, "called, offset={}", offset);
- void Start(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ const std::vector<u8> data{ctx.ReadBuffer()};
+
+ if (data.size() > backing.buffer.size() - offset) {
+ LOG_ERROR(Service_AM,
+ "offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
+ backing.buffer.size(), data.size(), offset);
- LOG_WARNING(Service_AM, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
}
- void PushInData(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- storage_stack.push(rp.PopIpcInterface<AM::IStorage>());
+ std::memcpy(backing.buffer.data() + offset, data.data(), data.size());
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- LOG_DEBUG(Service_AM, "called");
- }
+void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
- void PopOutData(Kernel::HLERequestContext& ctx) {
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<AM::IStorage>(std::move(storage_stack.top()));
+ const u64 offset{rp.Pop<u64>()};
+ LOG_DEBUG(Service_AM, "called, offset={}", offset);
- storage_stack.pop();
+ const std::size_t size{ctx.GetWriteBufferSize()};
- LOG_DEBUG(Service_AM, "called");
+ if (size > backing.buffer.size() - offset) {
+ LOG_ERROR(Service_AM, "offset is out of bounds, backing_buffer_sz={}, size={}, offset={}",
+ backing.buffer.size(), size, offset);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_SIZE_OUT_OF_BOUNDS);
}
- std::stack<std::shared_ptr<AM::IStorage>> storage_stack;
- Kernel::SharedPtr<Kernel::Event> state_changed_event;
-};
+ ctx.WriteBuffer(backing.buffer.data() + offset, size);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryAppletCreator") {
static const FunctionInfo functions[] = {
@@ -632,7 +768,7 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
{1, nullptr, "TerminateAllLibraryApplets"},
{2, nullptr, "AreAnyLibraryAppletsLeft"},
{10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
- {11, nullptr, "CreateTransferMemoryStorage"},
+ {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
{12, nullptr, "CreateHandleStorage"},
};
RegisterHandlers(functions);
@@ -640,25 +776,81 @@ ILibraryAppletCreator::ILibraryAppletCreator() : ServiceFramework("ILibraryApple
ILibraryAppletCreator::~ILibraryAppletCreator() = default;
+static std::shared_ptr<Applets::Applet> GetAppletFromId(AppletId id) {
+ switch (id) {
+ case AppletId::ProfileSelect:
+ return std::make_shared<Applets::ProfileSelect>();
+ case AppletId::SoftwareKeyboard:
+ return std::make_shared<Applets::SoftwareKeyboard>();
+ default:
+ LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
+ static_cast<u32>(id));
+ return std::make_shared<Applets::StubApplet>();
+ }
+}
+
void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_id = rp.PopRaw<AppletId>();
+ const auto applet_mode = rp.PopRaw<u32>();
+
+ LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}",
+ static_cast<u32>(applet_id), applet_mode);
+
+ const auto applet = GetAppletFromId(applet_id);
+
+ if (applet == nullptr) {
+ LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", static_cast<u32>(applet_id));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(-1));
+ return;
+ }
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<AM::ILibraryAppletAccessor>();
-
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<AM::ILibraryAppletAccessor>(applet);
}
void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size{rp.Pop<u64>()};
+ LOG_DEBUG(Service_AM, "called, size={}", size);
+
std::vector<u8> buffer(size);
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<AM::IStorage>(std::move(buffer));
+}
- LOG_DEBUG(Service_AM, "called, size={}", size);
+void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
+ IPC::RequestParser rp{ctx};
+
+ rp.SetCurrentOffset(3);
+ const auto handle{rp.Pop<Kernel::Handle>()};
+
+ const auto shared_mem =
+ Core::System::GetInstance().CurrentProcess()->GetHandleTable().Get<Kernel::SharedMemory>(
+ handle);
+
+ if (shared_mem == nullptr) {
+ LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(-1));
+ return;
+ }
+
+ const u8* mem_begin = shared_mem->GetPointer();
+ const u8* mem_end = mem_begin + shared_mem->GetSize();
+ std::vector<u8> memory{mem_begin, mem_end};
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(std::make_shared<IStorage>(std::move(memory)));
}
IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationFunctions") {
@@ -674,8 +866,8 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{22, &IApplicationFunctions::SetTerminateResult, "SetTerminateResult"},
{23, &IApplicationFunctions::GetDisplayVersion, "GetDisplayVersion"},
{24, nullptr, "GetLaunchStorageInfoForDebug"},
- {25, nullptr, "ExtendSaveData"},
- {26, nullptr, "GetSaveDataSize"},
+ {25, &IApplicationFunctions::ExtendSaveData, "ExtendSaveData"},
+ {26, &IApplicationFunctions::GetSaveDataSize, "GetSaveDataSize"},
{30, &IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed, "BeginBlockingHomeButtonShortAndLongPressed"},
{31, &IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed, "EndBlockingHomeButtonShortAndLongPressed"},
{32, &IApplicationFunctions::BeginBlockingHomeButton, "BeginBlockingHomeButton"},
@@ -690,7 +882,7 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
{70, nullptr, "RequestToShutdown"},
{71, nullptr, "RequestToReboot"},
{80, nullptr, "ExitAndRequestToShowThanksMessage"},
- {90, nullptr, "EnableApplicationCrashReport"},
+ {90, &IApplicationFunctions::EnableApplicationCrashReport, "EnableApplicationCrashReport"},
{100, nullptr, "InitializeApplicationCopyrightFrameBuffer"},
{101, nullptr, "SetApplicationCopyrightImage"},
{102, nullptr, "SetApplicationCopyrightVisibility"},
@@ -709,33 +901,46 @@ IApplicationFunctions::IApplicationFunctions() : ServiceFramework("IApplicationF
IApplicationFunctions::~IApplicationFunctions() = default;
+void IApplicationFunctions::EnableApplicationCrashReport(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void IApplicationFunctions::BeginBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::EndBlockingHomeButtonShortAndLongPressed(
Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::BeginBlockingHomeButton(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::EndBlockingHomeButton(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
LaunchParameters params{};
params.magic = POP_LAUNCH_PARAMETER_MAGIC;
@@ -743,7 +948,7 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
Account::ProfileManager profile_manager{};
const auto uuid = profile_manager.GetUser(Settings::values.current_user);
- ASSERT(uuid != std::nullopt);
+ ASSERT(uuid);
params.current_user = uuid->uuid;
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -754,21 +959,19 @@ void IApplicationFunctions::PopLaunchParameter(Kernel::HLERequestContext& ctx) {
std::memcpy(buffer.data(), &params, buffer.size());
rb.PushIpcInterface<AM::IStorage>(buffer);
-
- LOG_DEBUG(Service_AM, "called");
}
void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
u128 uid = rp.PopRaw<u128>(); // What does this do?
-
LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
IPC::ResponseBuilder rb{ctx, 4};
@@ -778,70 +981,119 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
// Takes an input u32 Result, no output.
- // For example, in some cases official apps use this with error 0x2A2 then uses svcBreak.
+ // For example, in some cases official apps use this with error 0x2A2 then
+ // uses svcBreak.
IPC::RequestParser rp{ctx};
u32 result = rp.Pop<u32>();
+ LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called, result=0x{:08X}", result);
}
void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);
rb.Push<u64>(0);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This should be configurable
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push(
static_cast<u64>(Service::Set::GetLanguageCodeFromIndex(Settings::values.language_index)));
- LOG_DEBUG(Service_AM, "called");
}
void IApplicationFunctions::InitializeGamePlayRecording(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::SetGamePlayRecordingState(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::NotifyRunning(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0); // Unknown, seems to be ignored by official processes
-
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
// Returns a 128-bit UUID
rb.Push<u64>(0);
rb.Push<u64>(0);
+}
- LOG_WARNING(Service_AM, "(STUBBED) called");
+void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto type{rp.PopRaw<FileSys::SaveDataType>()};
+ rp.Skip(1, false);
+ const auto user_id{rp.PopRaw<u128>()};
+ const auto new_normal_size{rp.PopRaw<u64>()};
+ const auto new_journal_size{rp.PopRaw<u64>()};
+
+ LOG_DEBUG(Service_AM,
+ "called with type={:02X}, user_id={:016X}{:016X}, new_normal={:016X}, "
+ "new_journal={:016X}",
+ static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
+
+ FileSystem::WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
+ {new_normal_size, new_journal_size});
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+
+ // The following value is used upon failure to help the system recover.
+ // Since we always succeed, this should be 0.
+ rb.Push<u64>(0);
+}
+
+void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto type{rp.PopRaw<FileSys::SaveDataType>()};
+ rp.Skip(1, false);
+ const auto user_id{rp.PopRaw<u128>()};
+
+ LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
+ user_id[1], user_id[0]);
+
+ const auto size =
+ FileSystem::ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
+
+ IPC::ResponseBuilder rb{ctx, 6};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(size.normal);
+ rb.Push(size.journal);
}
void InstallInterfaces(SM::ServiceManager& service_manager,
std::shared_ptr<NVFlinger::NVFlinger> nvflinger) {
- std::make_shared<AppletAE>(nvflinger)->InstallAsService(service_manager);
- std::make_shared<AppletOE>(nvflinger)->InstallAsService(service_manager);
+ auto message_queue = std::make_shared<AppletMessageQueue>();
+ message_queue->PushMessage(AppletMessageQueue::AppletMessage::FocusStateChanged); // Needed on
+ // game boot
+
+ std::make_shared<AppletAE>(nvflinger, message_queue)->InstallAsService(service_manager);
+ std::make_shared<AppletOE>(nvflinger, message_queue)->InstallAsService(service_manager);
std::make_shared<IdleSys>()->InstallAsService(service_manager);
std::make_shared<OMM>()->InstallAsService(service_manager);
std::make_shared<SPSM>()->InstallAsService(service_manager);
@@ -868,9 +1120,10 @@ IHomeMenuFunctions::IHomeMenuFunctions() : ServiceFramework("IHomeMenuFunctions"
IHomeMenuFunctions::~IHomeMenuFunctions() = default;
void IHomeMenuFunctions::RequestToGetForeground(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_AM, "(STUBBED) called");
}
IGlobalStateController::IGlobalStateController() : ServiceFramework("IGlobalStateController") {
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 095f94851..b6113cfdd 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -5,12 +5,10 @@
#pragma once
#include <memory>
+#include <queue>
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/service.h"
-namespace Kernel {
-class Event;
-}
-
namespace Service {
namespace NVFlinger {
class NVFlinger;
@@ -39,6 +37,31 @@ enum SystemLanguage {
TraditionalChinese = 16,
};
+class AppletMessageQueue {
+public:
+ enum class AppletMessage : u32 {
+ NoMessage = 0,
+ FocusStateChanged = 15,
+ OperationModeChanged = 30,
+ PerformanceModeChanged = 31,
+ };
+
+ AppletMessageQueue();
+ ~AppletMessageQueue();
+
+ const Kernel::SharedPtr<Kernel::ReadableEvent>& GetMesssageRecieveEvent() const;
+ const Kernel::SharedPtr<Kernel::ReadableEvent>& GetOperationModeChangedEvent() const;
+ void PushMessage(AppletMessage msg);
+ AppletMessage PopMessage();
+ std::size_t GetMessageCount() const;
+ void OperationModeChanged();
+
+private:
+ std::queue<AppletMessage> messages;
+ Kernel::EventPair on_new_message;
+ Kernel::EventPair on_operation_mode_changed;
+};
+
class IWindowController final : public ServiceFramework<IWindowController> {
public:
IWindowController();
@@ -96,13 +119,13 @@ private:
void GetIdleTimeDetectionExtension(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
- Kernel::SharedPtr<Kernel::Event> launchable_event;
+ Kernel::EventPair launchable_event;
u32 idle_time_detection_extension = 0;
};
class ICommonStateGetter final : public ServiceFramework<ICommonStateGetter> {
public:
- ICommonStateGetter();
+ explicit ICommonStateGetter(std::shared_ptr<AppletMessageQueue> msg_queue);
~ICommonStateGetter() override;
private:
@@ -125,7 +148,35 @@ private:
void GetBootMode(Kernel::HLERequestContext& ctx);
void GetDefaultDisplayResolution(Kernel::HLERequestContext& ctx);
- Kernel::SharedPtr<Kernel::Event> event;
+ std::shared_ptr<AppletMessageQueue> msg_queue;
+};
+
+class IStorage final : public ServiceFramework<IStorage> {
+public:
+ explicit IStorage(std::vector<u8> buffer);
+ ~IStorage() override;
+
+ const std::vector<u8>& GetData() const;
+
+private:
+ void Open(Kernel::HLERequestContext& ctx);
+
+ std::vector<u8> buffer;
+
+ friend class IStorageAccessor;
+};
+
+class IStorageAccessor final : public ServiceFramework<IStorageAccessor> {
+public:
+ explicit IStorageAccessor(IStorage& backing);
+ ~IStorageAccessor() override;
+
+private:
+ void GetSize(Kernel::HLERequestContext& ctx);
+ void Write(Kernel::HLERequestContext& ctx);
+ void Read(Kernel::HLERequestContext& ctx);
+
+ IStorage& backing;
};
class ILibraryAppletCreator final : public ServiceFramework<ILibraryAppletCreator> {
@@ -136,6 +187,7 @@ public:
private:
void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
void CreateStorage(Kernel::HLERequestContext& ctx);
+ void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
};
class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
@@ -154,10 +206,13 @@ private:
void SetGamePlayRecordingState(Kernel::HLERequestContext& ctx);
void NotifyRunning(Kernel::HLERequestContext& ctx);
void GetPseudoDeviceId(Kernel::HLERequestContext& ctx);
+ void ExtendSaveData(Kernel::HLERequestContext& ctx);
+ void GetSaveDataSize(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButtonShortAndLongPressed(Kernel::HLERequestContext& ctx);
void BeginBlockingHomeButton(Kernel::HLERequestContext& ctx);
void EndBlockingHomeButton(Kernel::HLERequestContext& ctx);
+ void EnableApplicationCrashReport(Kernel::HLERequestContext& ctx);
};
class IHomeMenuFunctions final : public ServiceFramework<IHomeMenuFunctions> {
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 68ea778e8..41a573a91 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -12,8 +12,10 @@ namespace Service::AM {
class ILibraryAppletProxy final : public ServiceFramework<ILibraryAppletProxy> {
public:
- explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
- : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)) {
+ explicit ILibraryAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue)
+ : ServiceFramework("ILibraryAppletProxy"), nvflinger(std::move(nvflinger)),
+ msg_queue(std::move(msg_queue)) {
static const FunctionInfo functions[] = {
{0, &ILibraryAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ILibraryAppletProxy::GetSelfController, "GetSelfController"},
@@ -30,75 +32,87 @@ public:
private:
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ICommonStateGetter>();
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetAudioController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetProcessWindingController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IProcessWindingController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
- LOG_DEBUG(Service_AM, "called");
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
- LOG_DEBUG(Service_AM, "called");
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationFunctions>();
- LOG_DEBUG(Service_AM, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
+ std::shared_ptr<AppletMessageQueue> msg_queue;
};
class ISystemAppletProxy final : public ServiceFramework<ISystemAppletProxy> {
public:
- explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
- : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)) {
+ explicit ISystemAppletProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue)
+ : ServiceFramework("ISystemAppletProxy"), nvflinger(std::move(nvflinger)),
+ msg_queue(std::move(msg_queue)) {
static const FunctionInfo functions[] = {
{0, &ISystemAppletProxy::GetCommonStateGetter, "GetCommonStateGetter"},
{1, &ISystemAppletProxy::GetSelfController, "GetSelfController"},
@@ -117,100 +131,116 @@ public:
private:
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ICommonStateGetter>();
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetAudioController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
- LOG_DEBUG(Service_AM, "called");
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
- LOG_DEBUG(Service_AM, "called");
}
void GetHomeMenuFunctions(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IHomeMenuFunctions>();
- LOG_DEBUG(Service_AM, "called");
}
void GetGlobalStateController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IGlobalStateController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetApplicationCreator(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationCreator>();
- LOG_DEBUG(Service_AM, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
+ std::shared_ptr<AppletMessageQueue> msg_queue;
};
void AppletAE::OpenSystemAppletProxy(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemAppletProxy>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<ISystemAppletProxy>(nvflinger, msg_queue);
}
void AppletAE::OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
}
void AppletAE::OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<ILibraryAppletProxy>(nvflinger, msg_queue);
}
-AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
- : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)) {
+AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue)
+ : ServiceFramework("appletAE"), nvflinger(std::move(nvflinger)),
+ msg_queue(std::move(msg_queue)) {
// clang-format off
static const FunctionInfo functions[] = {
{100, &AppletAE::OpenSystemAppletProxy, "OpenSystemAppletProxy"},
@@ -228,4 +258,8 @@ AppletAE::AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
AppletAE::~AppletAE() = default;
+const std::shared_ptr<AppletMessageQueue>& AppletAE::GetMessageQueue() const {
+ return msg_queue;
+}
+
} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 1ed77baa4..902db2665 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -17,15 +17,19 @@ namespace AM {
class AppletAE final : public ServiceFramework<AppletAE> {
public:
- explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
+ explicit AppletAE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue);
~AppletAE() override;
+ const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
+
private:
void OpenSystemAppletProxy(Kernel::HLERequestContext& ctx);
void OpenLibraryAppletProxy(Kernel::HLERequestContext& ctx);
void OpenLibraryAppletProxyOld(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
+ std::shared_ptr<AppletMessageQueue> msg_queue;
};
} // namespace AM
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index 60717afd9..d3a0a1568 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -12,8 +12,10 @@ namespace Service::AM {
class IApplicationProxy final : public ServiceFramework<IApplicationProxy> {
public:
- explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
- : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)) {
+ explicit IApplicationProxy(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue)
+ : ServiceFramework("IApplicationProxy"), nvflinger(std::move(nvflinger)),
+ msg_queue(std::move(msg_queue)) {
// clang-format off
static const FunctionInfo functions[] = {
{0, &IApplicationProxy::GetCommonStateGetter, "GetCommonStateGetter"},
@@ -33,73 +35,85 @@ public:
private:
void GetAudioController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAudioController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetDisplayController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDisplayController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetDebugFunctions(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IDebugFunctions>();
- LOG_DEBUG(Service_AM, "called");
}
void GetWindowController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IWindowController>();
- LOG_DEBUG(Service_AM, "called");
}
void GetSelfController(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISelfController>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
}
void GetCommonStateGetter(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ICommonStateGetter>();
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<ICommonStateGetter>(msg_queue);
}
void GetLibraryAppletCreator(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILibraryAppletCreator>();
- LOG_DEBUG(Service_AM, "called");
}
void GetApplicationFunctions(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IApplicationFunctions>();
- LOG_DEBUG(Service_AM, "called");
}
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
+ std::shared_ptr<AppletMessageQueue> msg_queue;
};
void AppletOE::OpenApplicationProxy(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IApplicationProxy>(nvflinger);
- LOG_DEBUG(Service_AM, "called");
+ rb.PushIpcInterface<IApplicationProxy>(nvflinger, msg_queue);
}
-AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
- : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)) {
+AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue)
+ : ServiceFramework("appletOE"), nvflinger(std::move(nvflinger)),
+ msg_queue(std::move(msg_queue)) {
static const FunctionInfo functions[] = {
{0, &AppletOE::OpenApplicationProxy, "OpenApplicationProxy"},
};
@@ -108,4 +122,8 @@ AppletOE::AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger)
AppletOE::~AppletOE() = default;
+const std::shared_ptr<AppletMessageQueue>& AppletOE::GetMessageQueue() const {
+ return msg_queue;
+}
+
} // namespace Service::AM
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 60cfdfd9d..bbd0108ef 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -17,13 +17,17 @@ namespace AM {
class AppletOE final : public ServiceFramework<AppletOE> {
public:
- explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger);
+ explicit AppletOE(std::shared_ptr<NVFlinger::NVFlinger> nvflinger,
+ std::shared_ptr<AppletMessageQueue> msg_queue);
~AppletOE() override;
+ const std::shared_ptr<AppletMessageQueue>& GetMessageQueue() const;
+
private:
void OpenApplicationProxy(Kernel::HLERequestContext& ctx);
std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
+ std::shared_ptr<AppletMessageQueue> msg_queue;
};
} // namespace AM
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
new file mode 100644
index 000000000..a6064c63f
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -0,0 +1,114 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/assert.h"
+#include "core/core.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/server_session.h"
+#include "core/hle/kernel/writable_event.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+AppletDataBroker::AppletDataBroker() {
+ auto& kernel = Core::System::GetInstance().Kernel();
+ state_changed_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent");
+ pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent");
+ pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
+}
+
+AppletDataBroker::~AppletDataBroker() = default;
+
+std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToGame() {
+ if (out_channel.empty())
+ return nullptr;
+
+ auto out = std::move(out_channel.front());
+ out_channel.pop();
+ return out;
+}
+
+std::unique_ptr<IStorage> AppletDataBroker::PopNormalDataToApplet() {
+ if (in_channel.empty())
+ return nullptr;
+
+ auto out = std::move(in_channel.front());
+ in_channel.pop();
+ return out;
+}
+
+std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToGame() {
+ if (out_interactive_channel.empty())
+ return nullptr;
+
+ auto out = std::move(out_interactive_channel.front());
+ out_interactive_channel.pop();
+ return out;
+}
+
+std::unique_ptr<IStorage> AppletDataBroker::PopInteractiveDataToApplet() {
+ if (in_interactive_channel.empty())
+ return nullptr;
+
+ auto out = std::move(in_interactive_channel.front());
+ in_interactive_channel.pop();
+ return out;
+}
+
+void AppletDataBroker::PushNormalDataFromGame(IStorage storage) {
+ in_channel.push(std::make_unique<IStorage>(storage));
+}
+
+void AppletDataBroker::PushNormalDataFromApplet(IStorage storage) {
+ out_channel.push(std::make_unique<IStorage>(storage));
+ pop_out_data_event.writable->Signal();
+}
+
+void AppletDataBroker::PushInteractiveDataFromGame(IStorage storage) {
+ in_interactive_channel.push(std::make_unique<IStorage>(storage));
+}
+
+void AppletDataBroker::PushInteractiveDataFromApplet(IStorage storage) {
+ out_interactive_channel.push(std::make_unique<IStorage>(storage));
+ pop_interactive_out_data_event.writable->Signal();
+}
+
+void AppletDataBroker::SignalStateChanged() const {
+ state_changed_event.writable->Signal();
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetNormalDataEvent() const {
+ return pop_out_data_event.readable;
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetInteractiveDataEvent() const {
+ return pop_interactive_out_data_event.readable;
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> AppletDataBroker::GetStateChangedEvent() const {
+ return state_changed_event.readable;
+}
+
+Applet::Applet() = default;
+
+Applet::~Applet() = default;
+
+void Applet::Initialize() {
+ const auto common = broker.PopNormalDataToApplet();
+ ASSERT(common != nullptr);
+
+ const auto common_data = common->GetData();
+
+ ASSERT(common_data.size() >= sizeof(CommonArguments));
+ std::memcpy(&common_args, common_data.data(), sizeof(CommonArguments));
+
+ initialized = true;
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
new file mode 100644
index 000000000..37424c379
--- /dev/null
+++ b/src/core/hle/service/am/applets/applets.h
@@ -0,0 +1,109 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <queue>
+#include "common/swap.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/writable_event.h"
+
+union ResultCode;
+
+namespace Service::AM {
+
+class IStorage;
+
+namespace Applets {
+
+class AppletDataBroker final {
+public:
+ AppletDataBroker();
+ ~AppletDataBroker();
+
+ std::unique_ptr<IStorage> PopNormalDataToGame();
+ std::unique_ptr<IStorage> PopNormalDataToApplet();
+
+ std::unique_ptr<IStorage> PopInteractiveDataToGame();
+ std::unique_ptr<IStorage> PopInteractiveDataToApplet();
+
+ void PushNormalDataFromGame(IStorage storage);
+ void PushNormalDataFromApplet(IStorage storage);
+
+ void PushInteractiveDataFromGame(IStorage storage);
+ void PushInteractiveDataFromApplet(IStorage storage);
+
+ void SignalStateChanged() const;
+
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetNormalDataEvent() const;
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetInteractiveDataEvent() const;
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetStateChangedEvent() const;
+
+private:
+ // Queues are named from applet's perspective
+
+ // PopNormalDataToApplet and PushNormalDataFromGame
+ std::queue<std::unique_ptr<IStorage>> in_channel;
+
+ // PopNormalDataToGame and PushNormalDataFromApplet
+ std::queue<std::unique_ptr<IStorage>> out_channel;
+
+ // PopInteractiveDataToApplet and PushInteractiveDataFromGame
+ std::queue<std::unique_ptr<IStorage>> in_interactive_channel;
+
+ // PopInteractiveDataToGame and PushInteractiveDataFromApplet
+ std::queue<std::unique_ptr<IStorage>> out_interactive_channel;
+
+ Kernel::EventPair state_changed_event;
+
+ // Signaled on PushNormalDataFromApplet
+ Kernel::EventPair pop_out_data_event;
+
+ // Signaled on PushInteractiveDataFromApplet
+ Kernel::EventPair pop_interactive_out_data_event;
+};
+
+class Applet {
+public:
+ Applet();
+ virtual ~Applet();
+
+ virtual void Initialize();
+
+ virtual bool TransactionComplete() const = 0;
+ virtual ResultCode GetStatus() const = 0;
+ virtual void ExecuteInteractive() = 0;
+ virtual void Execute() = 0;
+
+ bool IsInitialized() const {
+ return initialized;
+ }
+
+ AppletDataBroker& GetBroker() {
+ return broker;
+ }
+
+ const AppletDataBroker& GetBroker() const {
+ return broker;
+ }
+
+protected:
+ struct CommonArguments {
+ u32_le arguments_version;
+ u32_le size;
+ u32_le library_version;
+ u32_le theme_color;
+ u8 play_startup_sound;
+ u64_le system_tick;
+ };
+ static_assert(sizeof(CommonArguments) == 0x20, "CommonArguments has incorrect size.");
+
+ CommonArguments common_args{};
+ AppletDataBroker broker;
+ bool initialized = false;
+};
+
+} // namespace Applets
+} // namespace Service::AM
diff --git a/src/core/hle/service/am/applets/profile_select.cpp b/src/core/hle/service/am/applets/profile_select.cpp
new file mode 100644
index 000000000..4c7b45454
--- /dev/null
+++ b/src/core/hle/service/am/applets/profile_select.cpp
@@ -0,0 +1,77 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+
+#include "common/assert.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/software_keyboard.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/profile_select.h"
+
+namespace Service::AM::Applets {
+
+constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
+
+ProfileSelect::ProfileSelect() = default;
+ProfileSelect::~ProfileSelect() = default;
+
+void ProfileSelect::Initialize() {
+ complete = false;
+ status = RESULT_SUCCESS;
+ final_data.clear();
+
+ Applet::Initialize();
+
+ const auto user_config_storage = broker.PopNormalDataToApplet();
+ ASSERT(user_config_storage != nullptr);
+ const auto& user_config = user_config_storage->GetData();
+
+ ASSERT(user_config.size() >= sizeof(UserSelectionConfig));
+ std::memcpy(&config, user_config.data(), sizeof(UserSelectionConfig));
+}
+
+bool ProfileSelect::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode ProfileSelect::GetStatus() const {
+ return status;
+}
+
+void ProfileSelect::ExecuteInteractive() {
+ UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+}
+
+void ProfileSelect::Execute() {
+ if (complete) {
+ broker.PushNormalDataFromApplet(IStorage{final_data});
+ return;
+ }
+
+ const auto& frontend{Core::System::GetInstance().GetProfileSelector()};
+
+ frontend.SelectProfile([this](std::optional<Account::UUID> uuid) { SelectionComplete(uuid); });
+}
+
+void ProfileSelect::SelectionComplete(std::optional<Account::UUID> uuid) {
+ UserSelectionOutput output{};
+
+ if (uuid.has_value() && uuid->uuid != Account::INVALID_UUID) {
+ output.result = 0;
+ output.uuid_selected = uuid->uuid;
+ } else {
+ status = ERR_USER_CANCELLED_SELECTION;
+ output.result = ERR_USER_CANCELLED_SELECTION.raw;
+ output.uuid_selected = Account::INVALID_UUID;
+ }
+
+ final_data = std::vector<u8>(sizeof(UserSelectionOutput));
+ std::memcpy(final_data.data(), &output, final_data.size());
+ broker.PushNormalDataFromApplet(IStorage{final_data});
+ broker.SignalStateChanged();
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/profile_select.h b/src/core/hle/service/am/applets/profile_select.h
new file mode 100644
index 000000000..787485f22
--- /dev/null
+++ b/src/core/hle/service/am/applets/profile_select.h
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "core/hle/service/acc/profile_manager.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+struct UserSelectionConfig {
+ // TODO(DarkLordZach): RE this structure
+ // It seems to be flags and the like that determine the UI of the applet on the switch... from
+ // my research this is safe to ignore for now.
+ INSERT_PADDING_BYTES(0xA0);
+};
+static_assert(sizeof(UserSelectionConfig) == 0xA0, "UserSelectionConfig has incorrect size.");
+
+struct UserSelectionOutput {
+ u64 result;
+ u128 uuid_selected;
+};
+static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has incorrect size.");
+
+class ProfileSelect final : public Applet {
+public:
+ ProfileSelect();
+ ~ProfileSelect() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void SelectionComplete(std::optional<Account::UUID> uuid);
+
+private:
+ UserSelectionConfig config;
+ bool complete = false;
+ ResultCode status = RESULT_SUCCESS;
+ std::vector<u8> final_data;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.cpp b/src/core/hle/service/am/applets/software_keyboard.cpp
new file mode 100644
index 000000000..f255f74b5
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -0,0 +1,160 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include "common/assert.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/software_keyboard.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/software_keyboard.h"
+
+namespace Service::AM::Applets {
+
+constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
+constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
+constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
+constexpr bool INTERACTIVE_STATUS_OK = false;
+
+static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
+ KeyboardConfig config, std::u16string initial_text) {
+ Core::Frontend::SoftwareKeyboardParameters params{};
+
+ params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ config.submit_text.data(), config.submit_text.size());
+ params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+ config.header_text.data(), config.header_text.size());
+ params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
+ config.sub_text.size());
+ params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
+ config.guide_text.size());
+ params.initial_text = initial_text;
+ params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
+ params.password = static_cast<bool>(config.is_password);
+ params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
+ params.value = static_cast<u8>(config.keyset_disable_bitmask);
+
+ return params;
+}
+
+SoftwareKeyboard::SoftwareKeyboard() = default;
+
+SoftwareKeyboard::~SoftwareKeyboard() = default;
+
+void SoftwareKeyboard::Initialize() {
+ complete = false;
+ initial_text.clear();
+ final_data.clear();
+
+ Applet::Initialize();
+
+ const auto keyboard_config_storage = broker.PopNormalDataToApplet();
+ ASSERT(keyboard_config_storage != nullptr);
+ const auto& keyboard_config = keyboard_config_storage->GetData();
+
+ ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
+ std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
+
+ const auto work_buffer_storage = broker.PopNormalDataToApplet();
+ ASSERT(work_buffer_storage != nullptr);
+ const auto& work_buffer = work_buffer_storage->GetData();
+
+ if (config.initial_string_size == 0)
+ return;
+
+ std::vector<char16_t> string(config.initial_string_size);
+ std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
+ string.size() * 2);
+ initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
+}
+
+bool SoftwareKeyboard::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode SoftwareKeyboard::GetStatus() const {
+ return RESULT_SUCCESS;
+}
+
+void SoftwareKeyboard::ExecuteInteractive() {
+ if (complete)
+ return;
+
+ const auto storage = broker.PopInteractiveDataToApplet();
+ ASSERT(storage != nullptr);
+ const auto data = storage->GetData();
+ const auto status = static_cast<bool>(data[0]);
+
+ if (status == INTERACTIVE_STATUS_OK) {
+ complete = true;
+ } else {
+ const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
+
+ std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
+ std::memcpy(string.data(), data.data() + 4, string.size() * 2);
+ frontend.SendTextCheckDialog(
+ Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
+ [this] { broker.SignalStateChanged(); });
+ }
+}
+
+void SoftwareKeyboard::Execute() {
+ if (complete) {
+ broker.PushNormalDataFromApplet(IStorage{final_data});
+ return;
+ }
+
+ const auto& frontend{Core::System::GetInstance().GetSoftwareKeyboard()};
+
+ const auto parameters = ConvertToFrontendParameters(config, initial_text);
+
+ frontend.RequestText([this](std::optional<std::u16string> text) { WriteText(text); },
+ parameters);
+}
+
+void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
+ std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
+
+ if (text.has_value()) {
+ std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
+
+ if (config.utf_8) {
+ const u64 size = text->size() + 8;
+ const auto new_text = Common::UTF16ToUTF8(*text);
+
+ std::memcpy(output_sub.data(), &size, sizeof(u64));
+ std::memcpy(output_sub.data() + 8, new_text.data(),
+ std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
+
+ output_main[0] = INTERACTIVE_STATUS_OK;
+ std::memcpy(output_main.data() + 4, new_text.data(),
+ std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
+ } else {
+ const u64 size = text->size() * 2 + 8;
+ std::memcpy(output_sub.data(), &size, sizeof(u64));
+ std::memcpy(output_sub.data() + 8, text->data(),
+ std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
+
+ output_main[0] = INTERACTIVE_STATUS_OK;
+ std::memcpy(output_main.data() + 4, text->data(),
+ std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
+ }
+
+ complete = !config.text_check;
+ final_data = output_main;
+
+ if (complete) {
+ broker.PushNormalDataFromApplet(IStorage{output_main});
+ broker.SignalStateChanged();
+ } else {
+ broker.PushInteractiveDataFromApplet(IStorage{output_sub});
+ }
+ } else {
+ output_main[0] = 1;
+ complete = true;
+ broker.PushNormalDataFromApplet(IStorage{output_main});
+ broker.SignalStateChanged();
+ }
+}
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/software_keyboard.h b/src/core/hle/service/am/applets/software_keyboard.h
new file mode 100644
index 000000000..efd5753a1
--- /dev/null
+++ b/src/core/hle/service/am/applets/software_keyboard.h
@@ -0,0 +1,74 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string>
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "common/swap.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+enum class KeysetDisable : u32 {
+ Space = 0x02,
+ Address = 0x04,
+ Percent = 0x08,
+ Slashes = 0x10,
+ Numbers = 0x40,
+ DownloadCode = 0x80,
+};
+
+struct KeyboardConfig {
+ INSERT_PADDING_BYTES(4);
+ std::array<char16_t, 9> submit_text;
+ u16_le left_symbol_key;
+ u16_le right_symbol_key;
+ INSERT_PADDING_BYTES(1);
+ KeysetDisable keyset_disable_bitmask;
+ u32_le initial_cursor_position;
+ std::array<char16_t, 65> header_text;
+ std::array<char16_t, 129> sub_text;
+ std::array<char16_t, 257> guide_text;
+ u32_le length_limit;
+ INSERT_PADDING_BYTES(4);
+ u32_le is_password;
+ INSERT_PADDING_BYTES(5);
+ bool utf_8;
+ bool draw_background;
+ u32_le initial_string_offset;
+ u32_le initial_string_size;
+ u32_le user_dictionary_offset;
+ u32_le user_dictionary_size;
+ bool text_check;
+ u64_le text_check_callback;
+};
+static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
+
+class SoftwareKeyboard final : public Applet {
+public:
+ SoftwareKeyboard();
+ ~SoftwareKeyboard() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void WriteText(std::optional<std::u16string> text);
+
+private:
+ KeyboardConfig config;
+ std::u16string initial_text;
+ bool complete = false;
+ std::vector<u8> final_data;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.cpp b/src/core/hle/service/am/applets/stub_applet.cpp
new file mode 100644
index 000000000..ed166b87d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.cpp
@@ -0,0 +1,70 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <string>
+
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/stub_applet.h"
+
+namespace Service::AM::Applets {
+
+static void LogCurrentStorage(AppletDataBroker& broker, std::string prefix) {
+ std::unique_ptr<IStorage> storage = broker.PopNormalDataToApplet();
+ for (; storage != nullptr; storage = broker.PopNormalDataToApplet()) {
+ const auto data = storage->GetData();
+ LOG_INFO(Service_AM,
+ "called (STUBBED), during {} recieved normal data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexVectorToString(data));
+ }
+
+ storage = broker.PopInteractiveDataToApplet();
+ for (; storage != nullptr; storage = broker.PopInteractiveDataToApplet()) {
+ const auto data = storage->GetData();
+ LOG_INFO(Service_AM,
+ "called (STUBBED), during {} recieved interactive data with size={:08X}, data={}",
+ prefix, data.size(), Common::HexVectorToString(data));
+ }
+}
+
+StubApplet::StubApplet() = default;
+
+StubApplet::~StubApplet() = default;
+
+void StubApplet::Initialize() {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ Applet::Initialize();
+ LogCurrentStorage(broker, "Initialize");
+}
+
+bool StubApplet::TransactionComplete() const {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ return true;
+}
+
+ResultCode StubApplet::GetStatus() const {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ return RESULT_SUCCESS;
+}
+
+void StubApplet::ExecuteInteractive() {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ LogCurrentStorage(broker, "ExecuteInteractive");
+
+ broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.SignalStateChanged();
+}
+
+void StubApplet::Execute() {
+ LOG_WARNING(Service_AM, "called (STUBBED)");
+ LogCurrentStorage(broker, "Execute");
+
+ broker.PushNormalDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.PushInteractiveDataFromApplet(IStorage{std::vector<u8>(0x1000)});
+ broker.SignalStateChanged();
+}
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/stub_applet.h b/src/core/hle/service/am/applets/stub_applet.h
new file mode 100644
index 000000000..7d8dc968d
--- /dev/null
+++ b/src/core/hle/service/am/applets/stub_applet.h
@@ -0,0 +1,24 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+class StubApplet final : public Applet {
+public:
+ StubApplet();
+ ~StubApplet() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+};
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/aoc/aoc_u.cpp b/src/core/hle/service/aoc/aoc_u.cpp
index 54305cf05..b506bc3dd 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -13,11 +13,14 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/process.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/aoc/aoc_u.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
+#include "core/settings.h"
namespace Service::AOC {
@@ -32,14 +35,14 @@ static std::vector<u64> AccumulateAOCTitleIDs() {
std::vector<u64> add_on_content;
const auto rcu = FileSystem::GetUnionContents();
const auto list =
- rcu->ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
+ rcu.ListEntriesFilter(FileSys::TitleType::AOC, FileSys::ContentRecordType::Data);
std::transform(list.begin(), list.end(), std::back_inserter(add_on_content),
[](const FileSys::RegisteredCacheEntry& rce) { return rce.title_id; });
add_on_content.erase(
std::remove_if(
add_on_content.begin(), add_on_content.end(),
[&rcu](u64 tid) {
- return rcu->GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
+ return rcu.GetEntry(tid, FileSys::ContentRecordType::Data)->GetStatus() !=
Loader::ResultStatus::Success;
}),
add_on_content.end());
@@ -61,17 +64,26 @@ AOC_U::AOC_U() : ServiceFramework("aoc:u"), add_on_content(AccumulateAOCTitleIDs
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- aoc_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::Sticky,
- "GetAddOnContentListChanged:Event");
+ aoc_change_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ "GetAddOnContentListChanged:Event");
}
AOC_U::~AOC_U() = default;
void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AOC, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
+
+ const auto& disabled = Settings::values.disabled_addons[current];
+ if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end()) {
+ rb.Push<u32>(0);
+ return;
+ }
+
rb.Push<u32>(static_cast<u32>(
std::count_if(add_on_content.begin(), add_on_content.end(),
[current](u64 tid) { return CheckAOCTitleIDMatchesBase(tid, current); })));
@@ -82,6 +94,7 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
const auto offset = rp.PopRaw<u32>();
auto count = rp.PopRaw<u32>();
+ LOG_DEBUG(Service_AOC, "called with offset={}, count={}", offset, count);
const auto current = Core::System::GetInstance().CurrentProcess()->GetTitleID();
@@ -91,6 +104,10 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
out.push_back(static_cast<u32>(add_on_content[i] & 0x7FF));
}
+ const auto& disabled = Settings::values.disabled_addons[current];
+ if (std::find(disabled.begin(), disabled.end(), "DLC") != disabled.end())
+ out = {};
+
if (out.size() < offset) {
IPC::ResponseBuilder rb{ctx, 2};
// TODO(DarkLordZach): Find the correct error code.
@@ -110,6 +127,8 @@ void AOC_U::ListAddOnContent(Kernel::HLERequestContext& ctx) {
}
void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_AOC, "called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
const auto title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID();
@@ -128,7 +147,6 @@ void AOC_U::PrepareAddOnContent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto aoc_id = rp.PopRaw<u32>();
-
LOG_WARNING(Service_AOC, "(STUBBED) called with aoc_id={:08X}", aoc_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -140,7 +158,7 @@ void AOC_U::GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(aoc_change_event);
+ rb.PushCopyObjects(aoc_change_event.readable);
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/aoc/aoc_u.h b/src/core/hle/service/aoc/aoc_u.h
index 68d94fdaa..5effea730 100644
--- a/src/core/hle/service/aoc/aoc_u.h
+++ b/src/core/hle/service/aoc/aoc_u.h
@@ -6,6 +6,10 @@
#include "core/hle/service/service.h"
+namespace Kernel {
+class WritableEvent;
+}
+
namespace Service::AOC {
class AOC_U final : public ServiceFramework<AOC_U> {
@@ -21,7 +25,7 @@ private:
void GetAddOnContentListChangedEvent(Kernel::HLERequestContext& ctx);
std::vector<u64> add_on_content;
- Kernel::SharedPtr<Kernel::Event> aoc_change_event;
+ Kernel::EventPair aoc_change_event;
};
/// Registers all AOC services with the specified service manager.
diff --git a/src/core/hle/service/apm/interface.cpp b/src/core/hle/service/apm/interface.cpp
index c22bd3859..fcacbab72 100644
--- a/src/core/hle/service/apm/interface.cpp
+++ b/src/core/hle/service/apm/interface.cpp
@@ -40,24 +40,22 @@ private:
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
u32 config = rp.Pop<u32>();
+ LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode),
+ config);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_APM, "(STUBBED) called mode={} config={}", static_cast<u32>(mode),
- config);
}
void GetPerformanceConfiguration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto mode = static_cast<PerformanceMode>(rp.Pop<u32>());
+ LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode));
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(PerformanceConfiguration::Config1));
-
- LOG_WARNING(Service_APM, "(STUBBED) called mode={}", static_cast<u32>(mode));
}
};
@@ -73,11 +71,11 @@ APM::APM(std::shared_ptr<Module> apm, const char* name)
APM::~APM() = default;
void APM::OpenSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_APM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
-
- LOG_DEBUG(Service_APM, "called");
}
APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
@@ -98,11 +96,11 @@ APM_Sys::APM_Sys() : ServiceFramework{"apm:sys"} {
APM_Sys::~APM_Sys() = default;
void APM_Sys::GetPerformanceEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_APM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISession>();
-
- LOG_DEBUG(Service_APM, "called");
}
} // namespace Service::APM
diff --git a/src/core/hle/service/arp/arp.cpp b/src/core/hle/service/arp/arp.cpp
index 358ef2576..e675b0188 100644
--- a/src/core/hle/service/arp/arp.cpp
+++ b/src/core/hle/service/arp/arp.cpp
@@ -59,11 +59,11 @@ public:
private:
void AcquireRegistrar(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_ARP, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRegistrar>();
-
- LOG_DEBUG(Service_ARP, "called");
}
};
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp
index ff1edefbb..dc6a6b188 100644
--- a/src/core/hle/service/audio/audout_u.cpp
+++ b/src/core/hle/service/audio/audout_u.cpp
@@ -13,8 +13,10 @@
#include "common/swap.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/audio/audout_u.h"
#include "core/memory.h"
@@ -44,8 +46,10 @@ enum class AudioState : u32 {
class IAudioOut final : public ServiceFramework<IAudioOut> {
public:
- IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core)
- : ServiceFramework("IAudioOut"), audio_core(audio_core), audio_params(audio_params) {
+ IAudioOut(AudoutParams audio_params, AudioCore::AudioOut& audio_core, std::string&& device_name,
+ std::string&& unique_name)
+ : ServiceFramework("IAudioOut"), audio_core(audio_core),
+ device_name(std::move(device_name)), audio_params(audio_params) {
static const FunctionInfo functions[] = {
{0, &IAudioOut::GetAudioOutState, "GetAudioOutState"},
@@ -65,11 +69,12 @@ public:
// This is the event handle used to check if the audio buffer was released
auto& kernel = Core::System::GetInstance().Kernel();
- buffer_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioOutBufferReleased");
+ buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ "IAudioOutBufferReleased");
stream = audio_core.OpenStream(audio_params.sample_rate, audio_params.channel_count,
- "IAudioOut", [=]() { buffer_event->Signal(); });
+ std::move(unique_name),
+ [=]() { buffer_event.writable->Signal(); });
}
private:
@@ -84,6 +89,7 @@ private:
void GetAudioOutState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(stream->IsPlaying() ? AudioState::Started : AudioState::Stopped));
@@ -118,7 +124,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(buffer_event);
+ rb.PushCopyObjects(buffer_event.readable);
}
void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
@@ -146,6 +152,7 @@ private:
void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called {}", ctx.Description());
+
IPC::RequestParser rp{ctx};
const u64 max_count{ctx.GetWriteBufferSize() / sizeof(u64)};
const auto released_buffers{audio_core.GetTagsAndReleaseBuffers(stream, max_count)};
@@ -161,6 +168,7 @@ private:
void ContainsAudioOutBuffer(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
const u64 tag{rp.Pop<u64>()};
IPC::ResponseBuilder rb{ctx, 3};
@@ -170,6 +178,7 @@ private:
void GetAudioOutBufferCount(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(static_cast<u32>(stream->GetQueueSize()));
@@ -177,15 +186,17 @@ private:
AudioCore::AudioOut& audio_core;
AudioCore::StreamPtr stream;
+ std::string device_name;
AudoutParams audio_params{};
- /// This is the evend handle used to check if the audio buffer was released
- Kernel::SharedPtr<Kernel::Event> buffer_event;
+ /// This is the event handle used to check if the audio buffer was released
+ Kernel::EventPair buffer_event;
};
void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
ctx.WriteBuffer(DefaultDevice);
@@ -199,7 +210,15 @@ void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) {
void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "called");
- ctx.WriteBuffer(DefaultDevice);
+ const auto device_name_data{ctx.ReadBuffer()};
+ std::string device_name;
+ if (device_name_data[0] != '\0') {
+ device_name.assign(device_name_data.begin(), device_name_data.end());
+ } else {
+ device_name.assign(DefaultDevice.begin(), DefaultDevice.end());
+ }
+ ctx.WriteBuffer(device_name);
+
IPC::RequestParser rp{ctx};
auto params{rp.PopRaw<AudoutParams>()};
if (params.channel_count <= 2) {
@@ -212,10 +231,9 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
params.sample_rate = DefaultSampleRate;
}
- // TODO(bunnei): Support more than one IAudioOut interface. When we add this, ListAudioOutsImpl
- // will likely need to be updated as well.
- ASSERT_MSG(!audio_out_interface, "Unimplemented");
- audio_out_interface = std::make_shared<IAudioOut>(params, *audio_core);
+ std::string unique_name{fmt::format("{}-{}", device_name, audio_out_interfaces.size())};
+ auto audio_out_interface = std::make_shared<IAudioOut>(
+ params, *audio_core, std::move(device_name), std::move(unique_name));
IPC::ResponseBuilder rb{ctx, 6, 0, 1};
rb.Push(RESULT_SUCCESS);
@@ -224,6 +242,8 @@ void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(static_cast<u32>(AudioCore::Codec::PcmFormat::Int16));
rb.Push<u32>(static_cast<u32>(AudioState::Stopped));
rb.PushIpcInterface<Audio::IAudioOut>(audio_out_interface);
+
+ audio_out_interfaces.push_back(std::move(audio_out_interface));
}
AudOutU::AudOutU() : ServiceFramework("audout:u") {
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h
index dcaf64708..aed4c43b2 100644
--- a/src/core/hle/service/audio/audout_u.h
+++ b/src/core/hle/service/audio/audout_u.h
@@ -4,6 +4,7 @@
#pragma once
+#include <vector>
#include "core/hle/service/service.h"
namespace AudioCore {
@@ -24,7 +25,7 @@ public:
~AudOutU() override;
private:
- std::shared_ptr<IAudioOut> audio_out_interface;
+ std::vector<std::shared_ptr<IAudioOut>> audio_out_interfaces;
std::unique_ptr<AudioCore::AudioOut> audio_core;
void ListAudioOutsImpl(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index fac6785a5..945259c7d 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -12,8 +12,10 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/audio/audren_u.h"
namespace Service::Audio {
@@ -28,90 +30,116 @@ public:
{1, &IAudioRenderer::GetSampleCount, "GetSampleCount"},
{2, &IAudioRenderer::GetMixBufferCount, "GetMixBufferCount"},
{3, &IAudioRenderer::GetState, "GetState"},
- {4, &IAudioRenderer::RequestUpdate, "RequestUpdate"},
+ {4, &IAudioRenderer::RequestUpdateImpl, "RequestUpdate"},
{5, &IAudioRenderer::Start, "Start"},
{6, &IAudioRenderer::Stop, "Stop"},
{7, &IAudioRenderer::QuerySystemEvent, "QuerySystemEvent"},
- {8, nullptr, "SetRenderingTimeLimit"},
- {9, nullptr, "GetRenderingTimeLimit"},
- {10, nullptr, "RequestUpdateAuto"},
+ {8, &IAudioRenderer::SetRenderingTimeLimit, "SetRenderingTimeLimit"},
+ {9, &IAudioRenderer::GetRenderingTimeLimit, "GetRenderingTimeLimit"},
+ {10, &IAudioRenderer::RequestUpdateImpl, "RequestUpdateAuto"},
{11, nullptr, "ExecuteAudioRendererRendering"},
};
// clang-format on
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- system_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "IAudioRenderer:SystemEvent");
- renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event);
+ system_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ "IAudioRenderer:SystemEvent");
+ renderer = std::make_unique<AudioCore::AudioRenderer>(audren_params, system_event.writable);
}
private:
void UpdateAudioCallback() {
- system_event->Signal();
+ system_event.writable->Signal();
}
void GetSampleRate(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetSampleRate());
- LOG_DEBUG(Service_Audio, "called");
}
void GetSampleCount(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetSampleCount());
- LOG_DEBUG(Service_Audio, "called");
}
void GetState(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(renderer->GetStreamState()));
- LOG_DEBUG(Service_Audio, "called");
}
void GetMixBufferCount(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(renderer->GetMixBufferCount());
- LOG_DEBUG(Service_Audio, "called");
}
- void RequestUpdate(Kernel::HLERequestContext& ctx) {
+ void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+
ctx.WriteBuffer(renderer->UpdateAudioRenderer(ctx.ReadBuffer()));
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void Start(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void Stop(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_WARNING(Service_Audio, "(STUBBED) called");
}
void QuerySystemEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(system_event);
+ rb.PushCopyObjects(system_event.readable);
+ }
- LOG_WARNING(Service_Audio, "(STUBBED) called");
+ void SetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rendering_time_limit_percent = rp.Pop<u32>();
+ LOG_DEBUG(Service_Audio, "called. rendering_time_limit_percent={}",
+ rendering_time_limit_percent);
+
+ ASSERT(rendering_time_limit_percent >= 0 && rendering_time_limit_percent <= 100);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
- Kernel::SharedPtr<Kernel::Event> system_event;
+ void GetRenderingTimeLimit(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(rendering_time_limit_percent);
+ }
+
+ Kernel::EventPair system_event;
std::unique_ptr<AudioCore::AudioRenderer> renderer;
+ u32 rendering_time_limit_percent = 100;
};
class IAudioDevice final : public ServiceFramework<IAudioDevice> {
@@ -136,8 +164,8 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- buffer_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
- "IAudioOutBufferReleasedEvent");
+ buffer_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "IAudioOutBufferReleasedEvent");
}
private:
@@ -181,21 +209,22 @@ private:
void QueryAudioDeviceSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
- buffer_event->Signal();
+ buffer_event.writable->Signal();
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(buffer_event);
+ rb.PushCopyObjects(buffer_event.readable);
}
void GetActiveChannelCount(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Audio, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
}
- Kernel::SharedPtr<Kernel::Event> buffer_event;
+ Kernel::EventPair buffer_event;
}; // namespace Audio
@@ -214,19 +243,20 @@ AudRenU::AudRenU() : ServiceFramework("audren:u") {
AudRenU::~AudRenU() = default;
void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<Audio::IAudioRenderer>(std::move(params));
-
- LOG_DEBUG(Service_Audio, "called");
}
void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
+ LOG_DEBUG(Service_Audio, "called");
u64 buffer_sz = Common::AlignUp(4 * params.mix_buffer_count, 0x40);
buffer_sz += params.unknown_c * 1024;
@@ -280,26 +310,26 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(output_sz);
- LOG_DEBUG(Service_Audio, "called, buffer_size=0x{:X}", output_sz);
+ LOG_DEBUG(Service_Audio, "buffer_size=0x{:X}", output_sz);
}
void AudRenU::GetAudioDevice(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Audio, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<Audio::IAudioDevice>();
-
- LOG_DEBUG(Service_Audio, "called");
}
void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Audio, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<Audio::IAudioDevice>();
-
- LOG_WARNING(Service_Audio, "(STUBBED) called"); // TODO(ogniK): Figure out what is different
- // based on the current revision
+ rb.PushIpcInterface<Audio::IAudioDevice>(); // TODO(ogniK): Figure out what is different
+ // based on the current revision
}
bool AudRenU::IsFeatureSupported(AudioFeatures feature, u32_le revision) const {
diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp
index 7168c6a10..a850cadc8 100644
--- a/src/core/hle/service/audio/hwopus.cpp
+++ b/src/core/hle/service/audio/hwopus.cpp
@@ -46,10 +46,13 @@ public:
private:
void DecodeInterleaved(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called");
+
u32 consumed = 0;
u32 sample_count = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples)) {
+ LOG_ERROR(Audio, "Failed to decode opus data");
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
@@ -63,12 +66,15 @@ private:
}
void DecodeInterleavedWithPerformance(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Audio, "called");
+
u32 consumed = 0;
u32 sample_count = 0;
u64 performance = 0;
std::vector<opus_int16> samples(ctx.GetWriteBufferSize() / sizeof(opus_int16));
if (!Decoder_DecodeInterleaved(consumed, sample_count, ctx.ReadBuffer(), samples,
performance)) {
+ LOG_ERROR(Audio, "Failed to decode opus data");
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
@@ -77,8 +83,8 @@ private:
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(consumed);
- rb.Push<u64>(performance);
rb.Push<u32>(sample_count);
+ rb.Push<u64>(performance);
ctx.WriteBuffer(samples.data(), samples.size() * sizeof(s16));
}
@@ -88,24 +94,39 @@ private:
std::optional<std::reference_wrapper<u64>> performance_time = std::nullopt) {
const auto start_time = std::chrono::high_resolution_clock::now();
std::size_t raw_output_sz = output.size() * sizeof(opus_int16);
- if (sizeof(OpusHeader) > input.size())
+ if (sizeof(OpusHeader) > input.size()) {
+ LOG_ERROR(Audio, "Input is smaller than the header size, header_sz={}, input_sz={}",
+ sizeof(OpusHeader), input.size());
return false;
+ }
OpusHeader hdr{};
std::memcpy(&hdr, input.data(), sizeof(OpusHeader));
if (sizeof(OpusHeader) + static_cast<u32>(hdr.sz) > input.size()) {
+ LOG_ERROR(Audio, "Input does not fit in the opus header size. data_sz={}, input_sz={}",
+ sizeof(OpusHeader) + static_cast<u32>(hdr.sz), input.size());
return false;
}
auto frame = input.data() + sizeof(OpusHeader);
auto decoded_sample_count = opus_packet_get_nb_samples(
frame, static_cast<opus_int32>(input.size() - sizeof(OpusHeader)),
static_cast<opus_int32>(sample_rate));
- if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz)
+ if (decoded_sample_count * channel_count * sizeof(u16) > raw_output_sz) {
+ LOG_ERROR(
+ Audio,
+ "Decoded data does not fit into the output data, decoded_sz={}, raw_output_sz={}",
+ decoded_sample_count * channel_count * sizeof(u16), raw_output_sz);
return false;
+ }
+ const int frame_size = (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count));
auto out_sample_count =
- opus_decode(decoder.get(), frame, hdr.sz, output.data(),
- (static_cast<int>(raw_output_sz / sizeof(s16) / channel_count)), 0);
- if (out_sample_count < 0)
+ opus_decode(decoder.get(), frame, hdr.sz, output.data(), frame_size, 0);
+ if (out_sample_count < 0) {
+ LOG_ERROR(Audio,
+ "Incorrect sample count received from opus_decode, "
+ "output_sample_count={}, frame_size={}, data_sz_from_hdr={}",
+ out_sample_count, frame_size, static_cast<u32>(hdr.sz));
return false;
+ }
const auto end_time = std::chrono::high_resolution_clock::now() - start_time;
sample_count = out_sample_count;
consumed = static_cast<u32>(sizeof(OpusHeader) + hdr.sz);
@@ -134,14 +155,17 @@ static std::size_t WorkerBufferSize(u32 channel_count) {
void HwOpus::GetWorkBufferSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto sample_rate = rp.Pop<u32>();
- auto channel_count = rp.Pop<u32>();
+ const auto sample_rate = rp.Pop<u32>();
+ const auto channel_count = rp.Pop<u32>();
+ LOG_DEBUG(Audio, "called with sample_rate={}, channel_count={}", sample_rate, channel_count);
+
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
"Invalid sample rate");
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
- u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
- LOG_DEBUG(Audio, "called worker_buffer_sz={}", worker_buffer_sz);
+
+ const u32 worker_buffer_sz = static_cast<u32>(WorkerBufferSize(channel_count));
+ LOG_DEBUG(Audio, "worker_buffer_sz={}", worker_buffer_sz);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -155,16 +179,18 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) {
auto buffer_sz = rp.Pop<u32>();
LOG_DEBUG(Audio, "called sample_rate={}, channel_count={}, buffer_size={}", sample_rate,
channel_count, buffer_sz);
+
ASSERT_MSG(sample_rate == 48000 || sample_rate == 24000 || sample_rate == 16000 ||
sample_rate == 12000 || sample_rate == 8000,
"Invalid sample rate");
ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count");
std::size_t worker_sz = WorkerBufferSize(channel_count);
- ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large");
+ ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large");
std::unique_ptr<OpusDecoder, OpusDeleter> decoder{
static_cast<OpusDecoder*>(operator new(worker_sz))};
- if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
+ if (const int err = opus_decoder_init(decoder.get(), sample_rate, channel_count)) {
+ LOG_ERROR(Audio, "Failed to init opus decoder with error={}", err);
IPC::ResponseBuilder rb{ctx, 2};
// TODO(ogniK): Use correct error code
rb.Push(ResultCode(-1));
diff --git a/src/core/hle/service/bcat/module.cpp b/src/core/hle/service/bcat/module.cpp
index 6e7b795fb..b7bd738fc 100644
--- a/src/core/hle/service/bcat/module.cpp
+++ b/src/core/hle/service/bcat/module.cpp
@@ -33,10 +33,11 @@ public:
};
void Module::Interface::CreateBcatService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BCAT, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IBcatService>();
- LOG_DEBUG(Service_BCAT, "called");
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
diff --git a/src/core/hle/service/btdrv/btdrv.cpp b/src/core/hle/service/btdrv/btdrv.cpp
index d0a15cc4c..5704ca0ab 100644
--- a/src/core/hle/service/btdrv/btdrv.cpp
+++ b/src/core/hle/service/btdrv/btdrv.cpp
@@ -2,12 +2,54 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/btdrv/btdrv.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
namespace Service::BtDrv {
+class Bt final : public ServiceFramework<Bt> {
+public:
+ explicit Bt() : ServiceFramework{"bt"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, nullptr, "Unknown0"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "Unknown3"},
+ {4, nullptr, "Unknown4"},
+ {5, nullptr, "Unknown5"},
+ {6, nullptr, "Unknown6"},
+ {7, nullptr, "Unknown7"},
+ {8, nullptr, "Unknown8"},
+ {9, &Bt::RegisterEvent, "RegisterEvent"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ register_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "BT:RegisterEvent");
+ }
+
+private:
+ void RegisterEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(register_event.readable);
+ }
+
+ Kernel::EventPair register_event;
+};
+
class BtDrv final : public ServiceFramework<BtDrv> {
public:
explicit BtDrv() : ServiceFramework{"btdrv"} {
@@ -67,6 +109,7 @@ public:
void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<BtDrv>()->InstallAsService(sm);
+ std::make_shared<Bt>()->InstallAsService(sm);
}
} // namespace Service::BtDrv
diff --git a/src/core/hle/service/btm/btm.cpp b/src/core/hle/service/btm/btm.cpp
index b949bfabd..ef7398a23 100644
--- a/src/core/hle/service/btm/btm.cpp
+++ b/src/core/hle/service/btm/btm.cpp
@@ -7,12 +7,126 @@
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/btm/btm.h"
#include "core/hle/service/service.h"
-#include "core/hle/service/sm/sm.h"
namespace Service::BTM {
+class IBtmUserCore final : public ServiceFramework<IBtmUserCore> {
+public:
+ explicit IBtmUserCore() : ServiceFramework{"IBtmUserCore"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &IBtmUserCore::GetScanEvent, "GetScanEvent"},
+ {1, nullptr, "Unknown1"},
+ {2, nullptr, "Unknown2"},
+ {3, nullptr, "Unknown3"},
+ {4, nullptr, "Unknown4"},
+ {5, nullptr, "Unknown5"},
+ {6, nullptr, "Unknown6"},
+ {7, nullptr, "Unknown7"},
+ {8, nullptr, "Unknown8"},
+ {9, nullptr, "Unknown9"},
+ {10, nullptr, "Unknown10"},
+ {17, &IBtmUserCore::GetConnectionEvent, "GetConnectionEvent"},
+ {18, nullptr, "Unknown18"},
+ {19, nullptr, "Unknown19"},
+ {20, nullptr, "Unknown20"},
+ {21, nullptr, "Unknown21"},
+ {22, nullptr, "Unknown22"},
+ {23, nullptr, "Unknown23"},
+ {24, nullptr, "Unknown24"},
+ {25, nullptr, "Unknown25"},
+ {26, &IBtmUserCore::GetDiscoveryEvent, "AcquireBleServiceDiscoveryEventImpl"},
+ {27, nullptr, "Unknown27"},
+ {28, nullptr, "Unknown28"},
+ {29, nullptr, "Unknown29"},
+ {30, nullptr, "Unknown30"},
+ {31, nullptr, "Unknown31"},
+ {32, nullptr, "Unknown32"},
+ {33, &IBtmUserCore::GetConfigEvent, "GetConfigEvent"},
+ {34, nullptr, "Unknown34"},
+ {35, nullptr, "Unknown35"},
+ {36, nullptr, "Unknown36"},
+ {37, nullptr, "Unknown37"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ scan_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "IBtmUserCore:ScanEvent");
+ connection_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot, "IBtmUserCore:ConnectionEvent");
+ service_discovery = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot, "IBtmUserCore:Discovery");
+ config_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "IBtmUserCore:ConfigEvent");
+ }
+
+private:
+ void GetScanEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(scan_event.readable);
+ }
+
+ void GetConnectionEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(connection_event.readable);
+ }
+
+ void GetDiscoveryEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(service_discovery.readable);
+ }
+
+ void GetConfigEvent(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_BTM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(config_event.readable);
+ }
+
+ Kernel::EventPair scan_event;
+ Kernel::EventPair connection_event;
+ Kernel::EventPair service_discovery;
+ Kernel::EventPair config_event;
+};
+
+class BTM_USR final : public ServiceFramework<BTM_USR> {
+public:
+ explicit BTM_USR() : ServiceFramework{"btm:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &BTM_USR::GetCoreImpl, "GetCoreImpl"},
+ };
+ // clang-format on
+ RegisterHandlers(functions);
+ }
+
+private:
+ void GetCoreImpl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BTM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IBtmUserCore>();
+ }
+};
+
class BTM final : public ServiceFramework<BTM> {
public:
explicit BTM() : ServiceFramework{"btm"} {
@@ -104,11 +218,11 @@ public:
private:
void GetCoreImpl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_BTM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IBtmSystemCore>();
-
- LOG_DEBUG(Service_BTM, "called");
}
};
@@ -116,6 +230,7 @@ void InstallInterfaces(SM::ServiceManager& sm) {
std::make_shared<BTM>()->InstallAsService(sm);
std::make_shared<BTM_DBG>()->InstallAsService(sm);
std::make_shared<BTM_SYS>()->InstallAsService(sm);
+ std::make_shared<BTM_USR>()->InstallAsService(sm);
}
} // namespace Service::BTM
diff --git a/src/core/hle/service/erpt/erpt.cpp b/src/core/hle/service/erpt/erpt.cpp
index ee11cd78e..d9b32954e 100644
--- a/src/core/hle/service/erpt/erpt.cpp
+++ b/src/core/hle/service/erpt/erpt.cpp
@@ -17,11 +17,13 @@ public:
static const FunctionInfo functions[] = {
{0, nullptr, "SubmitContext"},
{1, nullptr, "CreateReport"},
- {2, nullptr, "Unknown1"},
- {3, nullptr, "Unknown2"},
- {4, nullptr, "Unknown3"},
- {5, nullptr, "Unknown4"},
- {6, nullptr, "Unknown5"},
+ {2, nullptr, "SetInitialLaunchSettingsCompletionTime"},
+ {3, nullptr, "ClearInitialLaunchSettingsCompletionTime"},
+ {4, nullptr, "UpdatePowerOnTime"},
+ {5, nullptr, "UpdateAwakeTime"},
+ {6, nullptr, "SubmitMultipleCategoryContext"},
+ {7, nullptr, "UpdateApplicationLaunchTime"},
+ {8, nullptr, "ClearApplicationLaunchTime"},
};
// clang-format on
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp
index 2f15ac2a6..770590d0b 100644
--- a/src/core/hle/service/fatal/fatal.cpp
+++ b/src/core/hle/service/fatal/fatal.cpp
@@ -111,7 +111,8 @@ static void GenerateErrorReport(ResultCode error_code, const FatalInfo& info) {
}
static void ThrowFatalError(ResultCode error_code, FatalType fatal_type, const FatalInfo& info) {
- LOG_ERROR(Service_Fatal, "Threw fatal error type {}", static_cast<u32>(fatal_type));
+ LOG_ERROR(Service_Fatal, "Threw fatal error type {} with error code 0x{:X}",
+ static_cast<u32>(fatal_type), error_code.raw);
switch (fatal_type) {
case FatalType::ErrorReportAndScreen:
GenerateErrorReport(error_code, info);
diff --git a/src/core/hle/service/fgm/fgm.cpp b/src/core/hle/service/fgm/fgm.cpp
index 566fbf924..e461274c1 100644
--- a/src/core/hle/service/fgm/fgm.cpp
+++ b/src/core/hle/service/fgm/fgm.cpp
@@ -42,11 +42,11 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FGM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRequest>();
-
- LOG_DEBUG(Service_FGM, "called");
}
};
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index e32a7c48e..c6da2df43 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -8,18 +8,23 @@
#include "common/file_util.h"
#include "core/core.h"
#include "core/file_sys/bis_factory.h"
+#include "core/file_sys/control_metadata.h"
#include "core/file_sys/errors.h"
#include "core/file_sys/mode.h"
+#include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/sdmc_factory.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/hle/service/filesystem/fsp_ldr.h"
#include "core/hle/service/filesystem/fsp_pr.h"
#include "core/hle/service/filesystem/fsp_srv.h"
+#include "core/loader/loader.h"
namespace Service::FileSystem {
@@ -28,6 +33,10 @@ namespace Service::FileSystem {
// TODO(DarkLordZach): Eventually make this configurable in settings.
constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
+// A default size for normal/journal save data size if application control metadata cannot be found.
+// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
+constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
+
static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base,
std::string_view dir_name_) {
std::string dir_name(FileUtil::SanitizePath(dir_name_));
@@ -113,6 +122,18 @@ ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::str
return RESULT_SUCCESS;
}
+ResultCode VfsDirectoryServiceWrapper::CleanDirectoryRecursively(const std::string& path) const {
+ const std::string sanitized_path(FileUtil::SanitizePath(path));
+ auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(sanitized_path));
+
+ if (!dir->CleanSubdirectoryRecursive(FileUtil::GetFilename(sanitized_path))) {
+ // TODO(DarkLordZach): Find a better error code for this
+ return ResultCode(-1);
+ }
+
+ return RESULT_SUCCESS;
+}
+
ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path_,
const std::string& dest_path_) const {
std::string src_path(FileUtil::SanitizePath(src_path_));
@@ -303,25 +324,73 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
static_cast<u8>(space), save_struct.DebugInfo());
if (save_data_factory == nullptr) {
- return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound);
+ return FileSys::ERROR_ENTITY_NOT_FOUND;
}
return save_data_factory->Open(space, save_struct);
}
+ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
+ LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
+
+ if (save_data_factory == nullptr) {
+ return FileSys::ERROR_ENTITY_NOT_FOUND;
+ }
+
+ return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
+}
+
ResultVal<FileSys::VirtualDir> OpenSDMC() {
LOG_TRACE(Service_FS, "Opening SDMC");
if (sdmc_factory == nullptr) {
- return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SdCardNotFound);
+ return FileSys::ERROR_SD_CARD_NOT_FOUND;
}
return sdmc_factory->Open();
}
-std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents() {
- return std::make_unique<FileSys::RegisteredCacheUnion>(std::vector<FileSys::RegisteredCache*>{
- GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()});
+FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
+ if (save_data_factory == nullptr) {
+ return {0, 0};
+ }
+
+ const auto value = save_data_factory->ReadSaveDataSize(type, title_id, user_id);
+
+ if (value.normal == 0 && value.journal == 0) {
+ FileSys::SaveDataSize new_size{SUFFICIENT_SAVE_DATA_SIZE, SUFFICIENT_SAVE_DATA_SIZE};
+
+ FileSys::NACP nacp;
+ const auto res = Core::System::GetInstance().GetAppLoader().ReadControlData(nacp);
+
+ if (res != Loader::ResultStatus::Success) {
+ FileSys::PatchManager pm{Core::CurrentProcess()->GetTitleID()};
+ auto [nacp_unique, discard] = pm.GetControlMetadata();
+
+ if (nacp_unique != nullptr) {
+ new_size = {nacp_unique->GetDefaultNormalSaveSize(),
+ nacp_unique->GetDefaultJournalSaveSize()};
+ }
+ } else {
+ new_size = {nacp.GetDefaultNormalSaveSize(), nacp.GetDefaultJournalSaveSize()};
+ }
+
+ WriteSaveDataSize(type, title_id, user_id, new_size);
+ return new_size;
+ }
+
+ return value;
+}
+
+void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
+ FileSys::SaveDataSize new_value) {
+ if (save_data_factory != nullptr)
+ save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
+}
+
+FileSys::RegisteredCacheUnion GetUnionContents() {
+ return FileSys::RegisteredCacheUnion{
+ {GetSystemNANDContents(), GetUserNANDContents(), GetSDMCContents()}};
}
FileSys::RegisteredCache* GetSystemNANDContents() {
@@ -360,6 +429,15 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
return bis_factory->GetModificationLoadRoot(title_id);
}
+FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
+ LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
+
+ if (bis_factory == nullptr)
+ return nullptr;
+
+ return bis_factory->GetModificationDumpRoot(title_id);
+}
+
void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
if (overwrite) {
bis_factory = nullptr;
@@ -373,13 +451,21 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
FileSys::Mode::ReadWrite);
auto load_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir),
FileSys::Mode::ReadWrite);
+ auto dump_directory = vfs.OpenDirectory(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir),
+ FileSys::Mode::ReadWrite);
- if (bis_factory == nullptr)
- bis_factory = std::make_unique<FileSys::BISFactory>(nand_directory, load_directory);
- if (save_data_factory == nullptr)
+ if (bis_factory == nullptr) {
+ bis_factory =
+ std::make_unique<FileSys::BISFactory>(nand_directory, load_directory, dump_directory);
+ }
+
+ if (save_data_factory == nullptr) {
save_data_factory = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory));
- if (sdmc_factory == nullptr)
+ }
+
+ if (sdmc_factory == nullptr) {
sdmc_factory = std::make_unique<FileSys::SDMCFactory>(std::move(sd_directory));
+ }
}
void InstallInterfaces(SM::ServiceManager& service_manager, FileSys::VfsFilesystem& vfs) {
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 6ca5c5636..6fd5e7b23 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -21,9 +21,11 @@ class SDMCFactory;
enum class ContentRecordType : u8;
enum class Mode : u32;
enum class SaveDataSpaceId : u8;
+enum class SaveDataType : u8;
enum class StorageId : u8;
struct SaveDataDescriptor;
+struct SaveDataSize;
} // namespace FileSys
namespace Service {
@@ -45,15 +47,21 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
FileSys::ContentRecordType type);
ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
FileSys::SaveDataDescriptor save_struct);
+ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
ResultVal<FileSys::VirtualDir> OpenSDMC();
-std::unique_ptr<FileSys::RegisteredCacheUnion> GetUnionContents();
+FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
+void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
+ FileSys::SaveDataSize new_value);
+
+FileSys::RegisteredCacheUnion GetUnionContents();
FileSys::RegisteredCache* GetSystemNANDContents();
FileSys::RegisteredCache* GetUserNANDContents();
FileSys::RegisteredCache* GetSDMCContents();
FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
+FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
// above is called.
@@ -111,6 +119,18 @@ public:
ResultCode DeleteDirectoryRecursively(const std::string& path) const;
/**
+ * Cleans the specified directory. This is similar to DeleteDirectoryRecursively,
+ * in that it deletes all the contents of the specified directory, however, this
+ * function does *not* delete the directory itself. It only deletes everything
+ * within it.
+ *
+ * @param path Path relative to the archive.
+ *
+ * @return Result of the operation.
+ */
+ ResultCode CleanDirectoryRecursively(const std::string& path) const;
+
+ /**
* Rename a File specified by its path
* @param src_path Source path relative to the archive
* @param dest_path Destination path relative to the archive
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index c1c83a11d..74c4e583b 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -11,6 +11,7 @@
#include "common/assert.h"
#include "common/common_types.h"
+#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
#include "core/file_sys/directory.h"
@@ -19,6 +20,7 @@
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/savedata_factory.h"
+#include "core/file_sys/system_archive/system_archive.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
@@ -43,8 +45,12 @@ public:
explicit IStorage(FileSys::VirtualFile backend_)
: ServiceFramework("IStorage"), backend(std::move(backend_)) {
static const FunctionInfo functions[] = {
- {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"},
- {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"},
+ {0, &IStorage::Read, "Read"},
+ {1, nullptr, "Write"},
+ {2, nullptr, "Flush"},
+ {3, nullptr, "SetSize"},
+ {4, &IStorage::GetSize, "GetSize"},
+ {5, nullptr, "OperateRange"},
};
RegisterHandlers(functions);
}
@@ -61,13 +67,15 @@ private:
// Error checking
if (length < 0) {
+ LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
+ rb.Push(FileSys::ERROR_INVALID_SIZE);
return;
}
if (offset < 0) {
+ LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
+ rb.Push(FileSys::ERROR_INVALID_OFFSET);
return;
}
@@ -79,6 +87,15 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ void GetSize(Kernel::HLERequestContext& ctx) {
+ const u64 size = backend->GetSize();
+ LOG_DEBUG(Service_FS, "called, size={}", size);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u64>(size);
+ }
};
class IFile final : public ServiceFramework<IFile> {
@@ -106,13 +123,15 @@ private:
// Error checking
if (length < 0) {
+ LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
+ rb.Push(FileSys::ERROR_INVALID_SIZE);
return;
}
if (offset < 0) {
+ LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
+ rb.Push(FileSys::ERROR_INVALID_OFFSET);
return;
}
@@ -137,13 +156,15 @@ private:
// Error checking
if (length < 0) {
+ LOG_ERROR(Service_FS, "Length is less than 0, length={}", length);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidLength));
+ rb.Push(FileSys::ERROR_INVALID_SIZE);
return;
}
if (offset < 0) {
+ LOG_ERROR(Service_FS, "Offset is less than 0, offset={}", offset);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, ErrorDescription::InvalidOffset));
+ rb.Push(FileSys::ERROR_INVALID_OFFSET);
return;
}
@@ -179,9 +200,10 @@ private:
void SetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 size = rp.Pop<u64>();
- backend->Resize(size);
LOG_DEBUG(Service_FS, "called, size={}", size);
+ backend->Resize(size);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -283,7 +305,7 @@ public:
{10, &IFileSystem::Commit, "Commit"},
{11, nullptr, "GetFreeSpaceSize"},
{12, nullptr, "GetTotalSpaceSize"},
- {13, nullptr, "CleanDirectoryRecursively"},
+ {13, &IFileSystem::CleanDirectoryRecursively, "CleanDirectoryRecursively"},
{14, nullptr, "GetFileTimeStampRaw"},
{15, nullptr, "QueryEntry"},
};
@@ -353,6 +375,16 @@ public:
rb.Push(backend.DeleteDirectoryRecursively(name));
}
+ void CleanDirectoryRecursively(Kernel::HLERequestContext& ctx) {
+ const auto file_buffer = ctx.ReadBuffer();
+ const std::string name = Common::StringFromBuffer(file_buffer);
+
+ LOG_DEBUG(Service_FS, "called. Directory: {}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(backend.CleanDirectoryRecursively(name));
+ }
+
void RenameFile(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
@@ -451,7 +483,149 @@ private:
VfsDirectoryServiceWrapper backend;
};
+class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
+public:
+ explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
+ : ServiceFramework("ISaveDataInfoReader") {
+ static const FunctionInfo functions[] = {
+ {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
+ };
+ RegisterHandlers(functions);
+
+ FindAllSaves(space);
+ }
+
+ void ReadSaveDataInfo(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_FS, "called");
+
+ // Calculate how many entries we can fit in the output buffer
+ const u64 count_entries = ctx.GetWriteBufferSize() / sizeof(SaveDataInfo);
+
+ // Cap at total number of entries.
+ const u64 actual_entries = std::min(count_entries, info.size() - next_entry_index);
+
+ // Determine data start and end
+ const auto* begin = reinterpret_cast<u8*>(info.data() + next_entry_index);
+ const auto* end = reinterpret_cast<u8*>(info.data() + next_entry_index + actual_entries);
+ const auto range_size = static_cast<std::size_t>(std::distance(begin, end));
+
+ next_entry_index += actual_entries;
+
+ // Write the data to memory
+ ctx.WriteBuffer(begin, range_size);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(actual_entries));
+ }
+
+private:
+ static u64 stoull_be(std::string_view str) {
+ if (str.size() != 16)
+ return 0;
+
+ const auto bytes = Common::HexStringToArray<0x8>(str);
+ u64 out{};
+ std::memcpy(&out, bytes.data(), sizeof(u64));
+
+ return Common::swap64(out);
+ }
+
+ void FindAllSaves(FileSys::SaveDataSpaceId space) {
+ const auto save_root = OpenSaveDataSpace(space);
+ ASSERT(save_root.Succeeded());
+
+ for (const auto& type : (*save_root)->GetSubdirectories()) {
+ if (type->GetName() == "save") {
+ for (const auto& save_id : type->GetSubdirectories()) {
+ for (const auto& user_id : save_id->GetSubdirectories()) {
+ const auto save_id_numeric = stoull_be(save_id->GetName());
+ auto user_id_numeric = Common::HexStringToArray<0x10>(user_id->GetName());
+ std::reverse(user_id_numeric.begin(), user_id_numeric.end());
+
+ if (save_id_numeric != 0) {
+ // System Save Data
+ info.emplace_back(SaveDataInfo{
+ 0,
+ space,
+ FileSys::SaveDataType::SystemSaveData,
+ {},
+ user_id_numeric,
+ save_id_numeric,
+ 0,
+ user_id->GetSize(),
+ {},
+ });
+
+ continue;
+ }
+
+ for (const auto& title_id : user_id->GetSubdirectories()) {
+ const auto device =
+ std::all_of(user_id_numeric.begin(), user_id_numeric.end(),
+ [](u8 val) { return val == 0; });
+ info.emplace_back(SaveDataInfo{
+ 0,
+ space,
+ device ? FileSys::SaveDataType::DeviceSaveData
+ : FileSys::SaveDataType::SaveData,
+ {},
+ user_id_numeric,
+ save_id_numeric,
+ stoull_be(title_id->GetName()),
+ title_id->GetSize(),
+ {},
+ });
+ }
+ }
+ }
+ } else if (space == FileSys::SaveDataSpaceId::TemporaryStorage) {
+ // Temporary Storage
+ for (const auto& user_id : type->GetSubdirectories()) {
+ for (const auto& title_id : user_id->GetSubdirectories()) {
+ if (!title_id->GetFiles().empty() ||
+ !title_id->GetSubdirectories().empty()) {
+ auto user_id_numeric =
+ Common::HexStringToArray<0x10>(user_id->GetName());
+ std::reverse(user_id_numeric.begin(), user_id_numeric.end());
+
+ info.emplace_back(SaveDataInfo{
+ 0,
+ space,
+ FileSys::SaveDataType::TemporaryStorage,
+ {},
+ user_id_numeric,
+ stoull_be(type->GetName()),
+ stoull_be(title_id->GetName()),
+ title_id->GetSize(),
+ {},
+ });
+ }
+ }
+ }
+ }
+ }
+ }
+
+ struct SaveDataInfo {
+ u64_le save_id_unknown;
+ FileSys::SaveDataSpaceId space;
+ FileSys::SaveDataType type;
+ INSERT_PADDING_BYTES(0x6);
+ std::array<u8, 0x10> user_id;
+ u64_le save_id;
+ u64_le title_id;
+ u64_le save_image_size;
+ INSERT_PADDING_BYTES(0x28);
+ };
+ static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
+
+ std::vector<SaveDataInfo> info;
+ u64 next_entry_index = 0;
+};
+
FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
+ // clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "MountContent"},
{1, &FSP_SRV::Initialize, "Initialize"},
@@ -485,7 +659,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{58, nullptr, "ReadSaveDataFileSystemExtraData"},
{59, nullptr, "WriteSaveDataFileSystemExtraData"},
{60, nullptr, "OpenSaveDataInfoReader"},
- {61, nullptr, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
+ {61, &FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId, "OpenSaveDataInfoReaderBySaveDataSpaceId"},
{62, nullptr, "OpenCacheStorageList"},
{64, nullptr, "OpenSaveDataInternalStorageFileSystem"},
{65, nullptr, "UpdateSaveDataMacForDebug"},
@@ -544,6 +718,7 @@ FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") {
{1009, nullptr, "GetAndClearMemoryReportInfo"},
{1100, nullptr, "OverrideSaveDataTransferTokenSignVerificationKey"},
};
+ // clang-format on
RegisterHandlers(functions);
}
@@ -561,6 +736,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
const auto type = rp.PopRaw<FileSystemType>();
const auto title_id = rp.PopRaw<u64>();
+ LOG_WARNING(Service_FS, "(STUBBED) called with type={}, title_id={:016X}",
+ static_cast<u8>(type), title_id);
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(ResultCode(-1));
@@ -596,13 +773,14 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) {
auto space_id = rp.PopRaw<FileSys::SaveDataSpaceId>();
auto unk = rp.Pop<u32>();
LOG_INFO(Service_FS, "called with unknown={:08X}", unk);
+
auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>();
auto dir = OpenSaveData(space_id, save_struct);
if (dir.Failed()) {
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
- rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
+ rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
return;
}
@@ -618,12 +796,31 @@ void FSP_SRV::OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx) {
MountSaveData(ctx);
}
+void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto space = rp.PopRaw<FileSys::SaveDataSpaceId>();
+ LOG_INFO(Service_FS, "called, space={}", static_cast<u8>(space));
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
+}
+
void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_FS, "(STUBBED) called");
+ enum class LogMode : u32 {
+ Off,
+ Log,
+ RedirectToSdCard,
+ LogToSdCard = Log | RedirectToSdCard,
+ };
+
+ // Given we always want to receive logging information,
+ // we always specify logging as enabled.
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(5);
+ rb.PushEnum(LogMode::Log);
}
void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
@@ -657,6 +854,15 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
if (data.Failed()) {
+ const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
+
+ if (archive != nullptr) {
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface(std::make_shared<IStorage>(archive));
+ return;
+ }
+
// TODO(DarkLordZach): Find the right error code to use here
LOG_ERROR(Service_FS,
"could not open data storage with title_id={:016X}, storage_id={:02X}", title_id,
@@ -685,7 +891,7 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) {
static_cast<u8>(storage_id), title_id);
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound));
+ rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
}
} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 4aa0358cb..e7abec0a3 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -25,6 +25,7 @@ private:
void CreateSaveData(Kernel::HLERequestContext& ctx);
void MountSaveData(Kernel::HLERequestContext& ctx);
void OpenReadOnlySaveDataFileSystem(Kernel::HLERequestContext& ctx);
+ void OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext& ctx);
void GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx);
void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx);
void OpenDataStorageByDataId(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/hid/controllers/debug_pad.cpp b/src/core/hle/service/hid/controllers/debug_pad.cpp
index 3d100763f..c22357d8c 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.cpp
+++ b/src/core/hle/service/hid/controllers/debug_pad.cpp
@@ -6,9 +6,14 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/debug_pad.h"
+#include "core/settings.h"
namespace Service::HID {
+constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
+constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
+enum class JoystickId : std::size_t { Joystick_Left, Joystick_Right };
+
Controller_DebugPad::Controller_DebugPad() = default;
Controller_DebugPad::~Controller_DebugPad() = default;
@@ -33,10 +38,44 @@ void Controller_DebugPad::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- // TODO(ogniK): Update debug pad states
+ cur_entry.attribute.connected.Assign(1);
+ auto& pad = cur_entry.pad_state;
+
+ using namespace Settings::NativeButton;
+ pad.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
+ pad.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
+
+ const auto [stick_l_x_f, stick_l_y_f] =
+ analogs[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+ const auto [stick_r_x_f, stick_r_y_f] =
+ analogs[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+ cur_entry.l_stick.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ cur_entry.l_stick.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ cur_entry.r_stick.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ cur_entry.r_stick.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
std::memcpy(data, &shared_memory, sizeof(SharedMemory));
}
-void Controller_DebugPad::OnLoadInputDevices() {}
+void Controller_DebugPad::OnLoadInputDevices() {
+ std::transform(Settings::values.debug_pad_buttons.begin(),
+ Settings::values.debug_pad_buttons.begin() +
+ Settings::NativeButton::NUM_BUTTONS_HID,
+ buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(Settings::values.debug_pad_analogs.begin(),
+ Settings::values.debug_pad_analogs.end(), analogs.begin(),
+ Input::CreateDevice<Input::AnalogDevice>);
+}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/debug_pad.h b/src/core/hle/service/hid/controllers/debug_pad.h
index 62b4f2682..68b734248 100644
--- a/src/core/hle/service/hid/controllers/debug_pad.h
+++ b/src/core/hle/service/hid/controllers/debug_pad.h
@@ -5,10 +5,13 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
namespace Service::HID {
class Controller_DebugPad final : public ControllerBase {
@@ -35,11 +38,40 @@ private:
};
static_assert(sizeof(AnalogStick) == 0x8);
+ struct PadState {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32_le> a;
+ BitField<1, 1, u32_le> b;
+ BitField<2, 1, u32_le> x;
+ BitField<3, 1, u32_le> y;
+ BitField<4, 1, u32_le> l;
+ BitField<5, 1, u32_le> r;
+ BitField<6, 1, u32_le> zl;
+ BitField<7, 1, u32_le> zr;
+ BitField<8, 1, u32_le> plus;
+ BitField<9, 1, u32_le> minus;
+ BitField<10, 1, u32_le> d_left;
+ BitField<11, 1, u32_le> d_up;
+ BitField<12, 1, u32_le> d_right;
+ BitField<13, 1, u32_le> d_down;
+ };
+ };
+ static_assert(sizeof(PadState) == 0x4, "PadState is an invalid size");
+
+ struct Attributes {
+ union {
+ u32_le raw{};
+ BitField<0, 1, u32_le> connected;
+ };
+ };
+ static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
+
struct PadStates {
s64_le sampling_number;
s64_le sampling_number2;
- u32_le attribute;
- u32_le button_state;
+ Attributes attribute;
+ PadState pad_state;
AnalogStick r_stick;
AnalogStick l_stick;
};
@@ -52,5 +84,10 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
+
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+ buttons;
+ std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>
+ analogs;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index ccfbce9ac..ca75adc2b 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -6,9 +6,11 @@
#include "common/common_types.h"
#include "core/core_timing.h"
#include "core/hle/service/hid/controllers/keyboard.h"
+#include "core/settings.h"
namespace Service::HID {
constexpr std::size_t SHARED_MEMORY_OFFSET = 0x3800;
+constexpr u8 KEYS_PER_BYTE = 8;
Controller_Keyboard::Controller_Keyboard() = default;
Controller_Keyboard::~Controller_Keyboard() = default;
@@ -34,10 +36,24 @@ void Controller_Keyboard::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- // TODO(ogniK): Update keyboard states
+
+ for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
+ for (std::size_t k = 0; k < KEYS_PER_BYTE; ++k) {
+ cur_entry.key[i / KEYS_PER_BYTE] |= (keyboard_keys[i]->GetStatus() << k);
+ }
+ }
+
+ for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
+ cur_entry.modifier |= (keyboard_mods[i]->GetStatus() << i);
+ }
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
-void Controller_Keyboard::OnLoadInputDevices() {}
+void Controller_Keyboard::OnLoadInputDevices() {
+ std::transform(Settings::values.keyboard_keys.begin(), Settings::values.keyboard_keys.end(),
+ keyboard_keys.begin(), Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(Settings::values.keyboard_mods.begin(), Settings::values.keyboard_mods.end(),
+ keyboard_mods.begin(), Input::CreateDevice<Input::ButtonDevice>);
+}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/keyboard.h b/src/core/hle/service/hid/controllers/keyboard.h
index 493e68fce..f52775456 100644
--- a/src/core/hle/service/hid/controllers/keyboard.h
+++ b/src/core/hle/service/hid/controllers/keyboard.h
@@ -8,7 +8,9 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
namespace Service::HID {
class Controller_Keyboard final : public ControllerBase {
@@ -46,5 +48,10 @@ private:
};
static_assert(sizeof(SharedMemory) == 0x400, "SharedMemory is an invalid size");
SharedMemory shared_memory{};
+
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardKeys>
+ keyboard_keys;
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeKeyboard::NumKeyboardMods>
+ keyboard_mods;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.cpp b/src/core/hle/service/hid/controllers/mouse.cpp
index 4e246a57d..63391dbe9 100644
--- a/src/core/hle/service/hid/controllers/mouse.cpp
+++ b/src/core/hle/service/hid/controllers/mouse.cpp
@@ -5,6 +5,7 @@
#include <cstring>
#include "common/common_types.h"
#include "core/core_timing.h"
+#include "core/frontend/emu_window.h"
#include "core/hle/service/hid/controllers/mouse.h"
namespace Service::HID {
@@ -14,7 +15,6 @@ Controller_Mouse::Controller_Mouse() = default;
Controller_Mouse::~Controller_Mouse() = default;
void Controller_Mouse::OnInit() {}
-
void Controller_Mouse::OnRelease() {}
void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
@@ -34,10 +34,29 @@ void Controller_Mouse::OnUpdate(u8* data, std::size_t size) {
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- // TODO(ogniK): Update mouse states
+
+ if (Settings::values.mouse_enabled) {
+ const auto [px, py, sx, sy] = mouse_device->GetStatus();
+ const auto x = static_cast<s32>(px * Layout::ScreenUndocked::Width);
+ const auto y = static_cast<s32>(py * Layout::ScreenUndocked::Height);
+ cur_entry.x = x;
+ cur_entry.y = y;
+ cur_entry.delta_x = x - last_entry.x;
+ cur_entry.delta_y = y - last_entry.y;
+ cur_entry.mouse_wheel_x = sx;
+ cur_entry.mouse_wheel_y = sy;
+
+ for (std::size_t i = 0; i < mouse_button_devices.size(); ++i) {
+ cur_entry.button |= (mouse_button_devices[i]->GetStatus() << i);
+ }
+ }
std::memcpy(data + SHARED_MEMORY_OFFSET, &shared_memory, sizeof(SharedMemory));
}
-void Controller_Mouse::OnLoadInputDevices() {}
+void Controller_Mouse::OnLoadInputDevices() {
+ mouse_device = Input::CreateDevice<Input::MouseDevice>(Settings::values.mouse_device);
+ std::transform(Settings::values.mouse_buttons.begin(), Settings::values.mouse_buttons.end(),
+ mouse_button_devices.begin(), Input::CreateDevice<Input::ButtonDevice>);
+}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/mouse.h b/src/core/hle/service/hid/controllers/mouse.h
index 543b0b71f..70b654d07 100644
--- a/src/core/hle/service/hid/controllers/mouse.h
+++ b/src/core/hle/service/hid/controllers/mouse.h
@@ -7,7 +7,9 @@
#include <array>
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/frontend/input.h"
#include "core/hle/service/hid/controllers/controller_base.h"
+#include "core/settings.h"
namespace Service::HID {
class Controller_Mouse final : public ControllerBase {
@@ -35,7 +37,8 @@ private:
s32_le y;
s32_le delta_x;
s32_le delta_y;
- s32_le mouse_wheel;
+ s32_le mouse_wheel_x;
+ s32_le mouse_wheel_y;
s32_le button;
s32_le attribute;
};
@@ -46,5 +49,9 @@ private:
std::array<MouseState, 17> mouse_states;
};
SharedMemory shared_memory{};
+
+ std::unique_ptr<Input::MouseDevice> mouse_device;
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeMouseButton::NumMouseButtons>
+ mouse_button_devices;
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 4b4d1324f..75fdb861a 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -12,27 +12,20 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/frontend/input.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "core/settings.h"
namespace Service::HID {
-
-constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
-constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
-constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
-constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr s32 HID_JOYSTICK_MIN = -0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
-constexpr u32 NPAD_HANDHELD = 32;
-constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
constexpr u32 MAX_NPAD_ID = 7;
-constexpr Controller_NPad::NPadControllerType PREFERRED_CONTROLLER =
- Controller_NPad::NPadControllerType::JoyDual;
constexpr std::array<u32, 10> npad_id_list{
- 0, 1, 2, 3, 4, 5, 6, 7, 32, 16,
+ 0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
enum class JoystickId : std::size_t {
@@ -40,6 +33,66 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
+static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
+ switch (type) {
+ case Settings::ControllerType::ProController:
+ return Controller_NPad::NPadControllerType::ProController;
+ case Settings::ControllerType::DualJoycon:
+ return Controller_NPad::NPadControllerType::JoyDual;
+ case Settings::ControllerType::LeftJoycon:
+ return Controller_NPad::NPadControllerType::JoyLeft;
+ case Settings::ControllerType::RightJoycon:
+ return Controller_NPad::NPadControllerType::JoyRight;
+ default:
+ UNREACHABLE();
+ return Controller_NPad::NPadControllerType::JoyDual;
+ }
+}
+
+std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
+ switch (npad_id) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return npad_id;
+ case 8:
+ case NPAD_HANDHELD:
+ return 8;
+ case 9:
+ case NPAD_UNKNOWN:
+ return 9;
+ default:
+ UNIMPLEMENTED_MSG("Unknown npad id {}", npad_id);
+ return 0;
+ }
+}
+
+u32 Controller_NPad::IndexToNPad(std::size_t index) {
+ switch (index) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ return static_cast<u32>(index);
+ case 8:
+ return NPAD_HANDHELD;
+ case 9:
+ return NPAD_UNKNOWN;
+ default:
+ UNIMPLEMENTED_MSG("Unknown npad index {}", index);
+ return 0;
+ };
+}
+
Controller_NPad::Controller_NPad() = default;
Controller_NPad::~Controller_NPad() = default;
@@ -56,22 +109,32 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.device_type.joycon_right.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
+ controller.properties.is_horizontal.Assign(1);
+ controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
+ controller.properties.is_horizontal.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.pad_assignment = NPadAssignments::Single;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
@@ -81,6 +144,9 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
case NPadControllerType::ProController:
controller.joy_styles.pro_controller.Assign(1);
controller.device_type.pro_controller.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
controller.pad_assignment = NPadAssignments::Single;
break;
}
@@ -90,14 +156,12 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
- controller.left_color.body_color = JOYCON_BODY_NEON_BLUE;
- controller.left_color.button_color = JOYCON_BUTTONS_NEON_BLUE;
- controller.right_color.body_color = JOYCON_BODY_NEON_RED;
- controller.right_color.button_color = JOYCON_BUTTONS_NEON_RED;
-
- controller.properties.is_vertical.Assign(1); // TODO(ogniK): Swap joycons orientations
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
+ controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
+ controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
+ controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
+ controller.right_color.button_color =
+ Settings::values.players[controller_idx].button_color_right;
+
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
@@ -105,8 +169,8 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
void Controller_NPad::OnInit() {
auto& kernel = Core::System::GetInstance().Kernel();
- styleset_changed_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
+ styleset_changed_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot, "npad:NpadStyleSetChanged");
if (!IsControllerActivated()) {
return;
@@ -121,26 +185,109 @@ void Controller_NPad::OnInit() {
style.pro_controller.Assign(1);
style.pokeball.Assign(1);
}
+
+ std::transform(
+ Settings::values.players.begin(), Settings::values.players.end(),
+ connected_controllers.begin(), [](const Settings::PlayerInput& player) {
+ return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
+ });
+
+ std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
+ [](const ControllerHolder& holder) { return holder.is_connected; });
+
+ // Account for handheld
+ if (connected_controllers[8].is_connected)
+ connected_controllers[8].type = NPadControllerType::Handheld;
+
+ supported_npad_id_types.resize(npad_id_list.size());
+ std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
+ npad_id_list.size() * sizeof(u32));
+
+ // Add a default dual joycon controller if none are present.
if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
[](const ControllerHolder& controller) { return controller.is_connected; })) {
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
- AddNewController(PREFERRED_CONTROLLER);
+ AddNewController(NPadControllerType::JoyDual);
+ }
+
+ for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
+ const auto& controller = connected_controllers[i];
+ if (controller.is_connected) {
+ AddNewControllerAt(controller.type, IndexToNPad(i));
+ }
}
}
void Controller_NPad::OnLoadInputDevices() {
- std::transform(Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
- Settings::values.buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
- buttons.begin(), Input::CreateDevice<Input::ButtonDevice>);
- std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
- Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
- sticks.begin(), Input::CreateDevice<Input::AnalogDevice>);
+ const auto& players = Settings::values.players;
+ for (std::size_t i = 0; i < players.size(); ++i) {
+ std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
+ players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
+ buttons[i].begin(), Input::CreateDevice<Input::ButtonDevice>);
+ std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
+ players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
+ sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+ }
}
void Controller_NPad::OnRelease() {}
+void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
+ const auto controller_idx = NPadIdToIndex(npad_id);
+ const auto controller_type = connected_controllers[controller_idx].type;
+ if (!connected_controllers[controller_idx].is_connected) {
+ return;
+ }
+ auto& pad_state = npad_pad_states[controller_idx].pad_states;
+ auto& lstick_entry = npad_pad_states[controller_idx].l_stick;
+ auto& rstick_entry = npad_pad_states[controller_idx].r_stick;
+ const auto& button_state = buttons[controller_idx];
+ const auto& analog_state = sticks[controller_idx];
+
+ using namespace Settings::NativeButton;
+ pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.l_stick_left.Assign(button_state[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_up.Assign(button_state[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_right.Assign(button_state[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick_down.Assign(button_state[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.r_stick_left.Assign(button_state[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_up.Assign(button_state[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_right.Assign(button_state[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick_down.Assign(button_state[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
+
+ const auto [stick_l_x_f, stick_l_y_f] =
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
+ const auto [stick_r_x_f, stick_r_y_f] =
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
+ lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+}
+
void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (!IsControllerActivated())
return;
@@ -176,97 +323,9 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
continue;
}
-
- // Pad states
- ControllerPadState pad_state{};
- using namespace Settings::NativeButton;
- pad_state.a.Assign(buttons[A - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.b.Assign(buttons[B - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.x.Assign(buttons[X - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.y.Assign(buttons[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick.Assign(buttons[LStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick.Assign(buttons[RStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l.Assign(buttons[L - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r.Assign(buttons[R - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zl.Assign(buttons[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zr.Assign(buttons[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.plus.Assign(buttons[Plus - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.minus.Assign(buttons[Minus - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.d_left.Assign(buttons[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_up.Assign(buttons[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_right.Assign(buttons[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_down.Assign(buttons[DDown - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.l_stick_left.Assign(buttons[LStick_Left - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick_up.Assign(buttons[LStick_Up - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick_right.Assign(buttons[LStick_Right - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick_down.Assign(buttons[LStick_Down - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.r_stick_left.Assign(buttons[RStick_Left - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick_up.Assign(buttons[RStick_Up - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick_right.Assign(buttons[RStick_Right - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick_down.Assign(buttons[RStick_Down - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.sl.Assign(buttons[SL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.sr.Assign(buttons[SR - BUTTON_HID_BEGIN]->GetStatus());
-
- AnalogPosition lstick_entry{};
- AnalogPosition rstick_entry{};
-
- const auto [stick_l_x_f, stick_l_y_f] =
- sticks[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetStatus();
- const auto [stick_r_x_f, stick_r_y_f] =
- sticks[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
- lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
-
- if (controller_type == NPadControllerType::JoyLeft ||
- controller_type == NPadControllerType::JoyRight) {
- if (npad.properties.is_horizontal) {
- ControllerPadState state{};
- AnalogPosition temp_lstick_entry{};
- AnalogPosition temp_rstick_entry{};
- if (controller_type == NPadControllerType::JoyLeft) {
- state.d_down.Assign(pad_state.d_left.Value());
- state.d_left.Assign(pad_state.d_up.Value());
- state.d_right.Assign(pad_state.d_down.Value());
- state.d_up.Assign(pad_state.d_right.Value());
- state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
- state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
-
- state.zl.Assign(pad_state.zl.Value());
- state.plus.Assign(pad_state.minus.Value());
-
- temp_lstick_entry = lstick_entry;
- temp_rstick_entry = rstick_entry;
- std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
- std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
- temp_lstick_entry.y *= -1;
- } else if (controller_type == NPadControllerType::JoyRight) {
- state.x.Assign(pad_state.a.Value());
- state.a.Assign(pad_state.b.Value());
- state.b.Assign(pad_state.y.Value());
- state.y.Assign(pad_state.b.Value());
-
- state.l.Assign(pad_state.l.Value() | pad_state.sl.Value());
- state.r.Assign(pad_state.r.Value() | pad_state.sr.Value());
- state.zr.Assign(pad_state.zr.Value());
- state.plus.Assign(pad_state.plus.Value());
-
- temp_lstick_entry = lstick_entry;
- temp_rstick_entry = rstick_entry;
- std::swap(temp_lstick_entry.x, temp_lstick_entry.y);
- std::swap(temp_rstick_entry.x, temp_rstick_entry.y);
- temp_rstick_entry.x *= -1;
- }
- pad_state.raw = state.raw;
- lstick_entry = temp_lstick_entry;
- rstick_entry = temp_rstick_entry;
- }
- }
+ const u32 npad_index = static_cast<u32>(i);
+ RequestPadStateUpdate(npad_index);
+ auto& pad_state = npad_pad_states[npad_index];
auto& main_controller =
npad.main_controller_states.npad[npad.main_controller_states.common.last_entry_index];
@@ -280,21 +339,19 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
npad.pokeball_states.npad[npad.pokeball_states.common.last_entry_index];
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
- if (hold_type == NpadHoldType::Horizontal) {
- // TODO(ogniK): Remap buttons for different orientations
- }
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
- handheld_entry.connection_status.IsConnected.Assign(1);
- if (!Settings::values.use_docked_mode) {
- handheld_entry.connection_status.IsWired.Assign(1);
- }
- handheld_entry.pad_states.raw = pad_state.raw;
- handheld_entry.l_stick = lstick_entry;
- handheld_entry.r_stick = rstick_entry;
+ handheld_entry.connection_status.IsWired.Assign(1);
+ handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
+ handheld_entry.connection_status.IsLeftJoyWired.Assign(1);
+ handheld_entry.connection_status.IsRightJoyWired.Assign(1);
+ handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ handheld_entry.pad.l_stick = pad_state.l_stick;
+ handheld_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
@@ -307,24 +364,25 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
libnx_entry.connection_status.IsConnected.Assign(1);
- dual_entry.pad_states.raw = pad_state.raw;
- dual_entry.l_stick = lstick_entry;
- dual_entry.r_stick = rstick_entry;
+ dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ dual_entry.pad.l_stick = pad_state.l_stick;
+ dual_entry.pad.r_stick = pad_state.r_stick;
+ break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
left_entry.connection_status.IsConnected.Assign(1);
- left_entry.pad_states.raw = pad_state.raw;
- left_entry.l_stick = lstick_entry;
- left_entry.r_stick = rstick_entry;
+ left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ left_entry.pad.l_stick = pad_state.l_stick;
+ left_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
right_entry.connection_status.IsConnected.Assign(1);
- right_entry.pad_states.raw = pad_state.raw;
- right_entry.l_stick = lstick_entry;
- right_entry.r_stick = rstick_entry;
+ right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ right_entry.pad.l_stick = pad_state.l_stick;
+ right_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
@@ -332,30 +390,30 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
pokeball_entry.connection_status.IsConnected.Assign(1);
pokeball_entry.connection_status.IsWired.Assign(1);
- pokeball_entry.pad_states.raw = pad_state.raw;
- pokeball_entry.l_stick = lstick_entry;
- pokeball_entry.r_stick = rstick_entry;
+ pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ pokeball_entry.pad.l_stick = pad_state.l_stick;
+ pokeball_entry.pad.r_stick = pad_state.r_stick;
break;
case NPadControllerType::ProController:
main_controller.connection_status.raw = 0;
main_controller.connection_status.IsConnected.Assign(1);
main_controller.connection_status.IsWired.Assign(1);
- main_controller.pad_states.raw = pad_state.raw;
- main_controller.l_stick = lstick_entry;
- main_controller.r_stick = rstick_entry;
+ main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
+ main_controller.pad.l_stick = pad_state.l_stick;
+ main_controller.pad.r_stick = pad_state.r_stick;
break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
// any controllers.
- libnx_entry.pad_states.raw = pad_state.raw;
- libnx_entry.l_stick = lstick_entry;
- libnx_entry.r_stick = rstick_entry;
+ libnx_entry.pad.pad_states.raw = pad_state.pad_states.raw;
+ libnx_entry.pad.l_stick = pad_state.l_stick;
+ libnx_entry.pad.r_stick = pad_state.r_stick;
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
-} // namespace Service::HID
+}
void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
style.raw = style_set.raw;
@@ -370,14 +428,29 @@ void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
+ bool had_controller_update = false;
for (std::size_t i = 0; i < connected_controllers.size(); i++) {
auto& controller = connected_controllers[i];
if (!controller.is_connected) {
continue;
}
- if (!IsControllerSupported(PREFERRED_CONTROLLER)) {
- controller.type = DecideBestController(PREFERRED_CONTROLLER);
- InitNewlyAddedControler(i);
+ const auto requested_controller =
+ i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
+ : NPadControllerType::Handheld;
+ if (!IsControllerSupported(requested_controller)) {
+ const auto is_handheld = requested_controller == NPadControllerType::Handheld;
+ if (is_handheld) {
+ controller.type = NPadControllerType::None;
+ controller.is_connected = false;
+ AddNewController(requested_controller);
+ } else {
+ controller.type = requested_controller;
+ InitNewlyAddedControler(i);
+ }
+ had_controller_update = true;
+ }
+ if (had_controller_update) {
+ styleset_changed_event.writable->Signal();
}
}
}
@@ -392,48 +465,49 @@ std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
}
void Controller_NPad::SetHoldType(NpadHoldType joy_hold_type) {
+ styleset_changed_event.writable->Signal();
hold_type = joy_hold_type;
}
+
Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
return hold_type;
}
void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
- ASSERT(npad_id < shared_memory_entries.size());
- shared_memory_entries[npad_id].pad_assignment = assignment_mode;
+ const std::size_t npad_index = NPadIdToIndex(npad_id);
+ ASSERT(npad_index < shared_memory_entries.size());
+ shared_memory_entries[npad_index].pad_assignment = assignment_mode;
}
void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
if (!can_controllers_vibrate) {
return;
}
for (std::size_t i = 0; i < controller_ids.size(); i++) {
- std::size_t controller_pos = i;
- // Handheld controller conversion
- if (controller_pos == NPAD_HANDHELD) {
- controller_pos = 8;
- }
- // Unknown controller conversion
- if (controller_pos == NPAD_UNKNOWN) {
- controller_pos = 9;
- }
+ std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
if (connected_controllers[controller_pos].is_connected) {
// TODO(ogniK): Vibrate the physical controller
}
}
- LOG_WARNING(Service_HID, "(STUBBED) called");
last_processed_vibration = vibrations.back();
}
-Kernel::SharedPtr<Kernel::Event> Controller_NPad::GetStyleSetChangedEvent() const {
- return styleset_changed_event;
+Kernel::SharedPtr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent() const {
+ // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
+ // be signalled at least once, and signaled after a new controller is connected?
+ styleset_changed_event.writable->Signal();
+ return styleset_changed_event.readable;
}
Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
return last_processed_vibration;
}
+
void Controller_NPad::AddNewController(NPadControllerType controller) {
+ controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
connected_controllers[8] = {controller, true};
InitNewlyAddedControler(8);
@@ -451,16 +525,54 @@ void Controller_NPad::AddNewController(NPadControllerType controller) {
InitNewlyAddedControler(controller_id);
}
-void Controller_NPad::ConnectNPad(u32 npad_id) {
- if (npad_id >= connected_controllers.size())
+void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
+ controller = DecideBestController(controller);
+ if (controller == NPadControllerType::Handheld) {
+ connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
+ InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
return;
- connected_controllers[npad_id].is_connected = true;
+ }
+
+ connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
+ InitNewlyAddedControler(NPadIdToIndex(npad_id));
+}
+
+void Controller_NPad::ConnectNPad(u32 npad_id) {
+ connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
}
void Controller_NPad::DisconnectNPad(u32 npad_id) {
- if (npad_id >= connected_controllers.size())
- return;
- connected_controllers[npad_id].is_connected = false;
+ connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
+}
+
+bool Controller_NPad::IsControllerSupported(NPadControllerType controller) {
+ if (controller == NPadControllerType::Handheld) {
+ // Handheld is not even a supported type, lets stop here
+ if (std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(),
+ NPAD_HANDHELD) == supported_npad_id_types.end()) {
+ return false;
+ }
+ // Handheld should not be supported in docked mode
+ if (Settings::values.use_docked_mode) {
+ return false;
+ }
+ }
+ switch (controller) {
+ case NPadControllerType::ProController:
+ return style.pro_controller;
+ case NPadControllerType::Handheld:
+ return style.handheld;
+ case NPadControllerType::JoyDual:
+ return style.joycon_dual;
+ case NPadControllerType::JoyLeft:
+ return style.joycon_left;
+ case NPadControllerType::JoyRight:
+ return style.joycon_right;
+ case NPadControllerType::Pokeball:
+ return style.pokeball;
+ default:
+ return false;
+ }
}
Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
@@ -494,6 +606,36 @@ void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
can_controllers_vibrate = can_vibrate;
}
+void Controller_NPad::ClearAllConnectedControllers() {
+ for (auto& controller : connected_controllers) {
+ if (controller.is_connected && controller.type != NPadControllerType::None) {
+ controller.type = NPadControllerType::None;
+ controller.is_connected = false;
+ }
+ }
+}
+void Controller_NPad::DisconnectAllConnectedControllers() {
+ std::for_each(connected_controllers.begin(), connected_controllers.end(),
+ [](ControllerHolder& controller) { controller.is_connected = false; });
+}
+
+void Controller_NPad::ConnectAllDisconnectedControllers() {
+ std::for_each(connected_controllers.begin(), connected_controllers.end(),
+ [](ControllerHolder& controller) {
+ if (controller.type != NPadControllerType::None && !controller.is_connected) {
+ controller.is_connected = false;
+ }
+ });
+}
+
+void Controller_NPad::ClearAllControllers() {
+ std::for_each(connected_controllers.begin(), connected_controllers.end(),
+ [](ControllerHolder& controller) {
+ controller.type = NPadControllerType::None;
+ controller.is_connected = false;
+ });
+}
+
bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const {
const bool support_handheld =
std::find(supported_npad_id_types.begin(), supported_npad_id_types.end(), NPAD_HANDHELD) !=
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index ac86985ff..29851f16a 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -5,13 +5,19 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_types.h"
#include "core/frontend/input.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/hid/controllers/controller_base.h"
#include "core/settings.h"
namespace Service::HID {
+constexpr u32 NPAD_HANDHELD = 32;
+constexpr u32 NPAD_UNKNOWN = 16; // TODO(ogniK): What is this?
+
class Controller_NPad final : public ControllerBase {
public:
Controller_NPad();
@@ -75,9 +81,9 @@ public:
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
position1.Assign(light1);
- position1.Assign(light2);
- position1.Assign(light3);
- position1.Assign(light4);
+ position2.Assign(light2);
+ position3.Assign(light3);
+ position4.Assign(light4);
}
union {
u64 raw{};
@@ -103,15 +109,23 @@ public:
void VibrateController(const std::vector<u32>& controller_ids,
const std::vector<Vibration>& vibrations);
- Kernel::SharedPtr<Kernel::Event> GetStyleSetChangedEvent() const;
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetStyleSetChangedEvent() const;
Vibration GetLastVibration() const;
void AddNewController(NPadControllerType controller);
+ void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
void ConnectNPad(u32 npad_id);
void DisconnectNPad(u32 npad_id);
LedPattern GetLedPattern(u32 npad_id);
void SetVibrationEnabled(bool can_vibrate);
+ void ClearAllConnectedControllers();
+ void DisconnectAllConnectedControllers();
+ void ConnectAllDisconnectedControllers();
+ void ClearAllControllers();
+
+ static std::size_t NPadIdToIndex(u32 npad_id);
+ static u32 IndexToNPad(std::size_t index);
private:
struct CommonHeader {
@@ -164,8 +178,11 @@ private:
BitField<23, 1, u64_le> r_stick_down;
// Not always active?
- BitField<24, 1, u64_le> sl;
- BitField<25, 1, u64_le> sr;
+ BitField<24, 1, u64_le> left_sl;
+ BitField<25, 1, u64_le> left_sr;
+
+ BitField<26, 1, u64_le> right_sl;
+ BitField<27, 1, u64_le> right_sr;
};
};
static_assert(sizeof(ControllerPadState) == 8, "ControllerPadState is an invalid size");
@@ -189,12 +206,17 @@ private:
};
static_assert(sizeof(ConnectionState) == 4, "ConnectionState is an invalid size");
- struct GenericStates {
- s64_le timestamp;
- s64_le timestamp2;
+ struct ControllerPad {
ControllerPadState pad_states;
AnalogPosition l_stick;
AnalogPosition r_stick;
+ };
+ static_assert(sizeof(ControllerPad) == 0x18, "ControllerPad is an invalid size");
+
+ struct GenericStates {
+ s64_le timestamp;
+ s64_le timestamp2;
+ ControllerPad pad;
ConnectionState connection_status;
};
static_assert(sizeof(GenericStates) == 0x30, "NPadGenericStates is an invalid size");
@@ -266,18 +288,23 @@ private:
static_assert(sizeof(NPadEntry) == 0x5000, "NPadEntry is an invalid size");
struct ControllerHolder {
- Controller_NPad::NPadControllerType type;
+ NPadControllerType type;
bool is_connected;
};
NPadType style{};
std::array<NPadEntry, 10> shared_memory_entries{};
- std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>
+ std::array<
+ std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
+ 10>
buttons;
- std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks;
+ std::array<
+ std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
+ 10>
+ sticks;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
- Kernel::SharedPtr<Kernel::Event> styleset_changed_event;
+ Kernel::EventPair styleset_changed_event;
Vibration last_processed_vibration{};
std::array<ControllerHolder, 10> connected_controllers{};
bool can_controllers_vibrate{true};
@@ -285,5 +312,8 @@ private:
void InitNewlyAddedControler(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
NPadControllerType DecideBestController(NPadControllerType priority) const;
+ void RequestPadStateUpdate(u32 npad_id);
+ std::array<ControllerPad, 10> npad_pad_states{};
+ bool IsControllerSupported(NPadControllerType controller);
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index 43efef803..f666b1bd8 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -41,16 +41,17 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
const auto [x, y, pressed] = touch_device->GetStatus();
auto& touch_entry = cur_entry.states[0];
- if (pressed) {
+ touch_entry.attribute.raw = 0;
+ if (pressed && Settings::values.touchscreen.enabled) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
- touch_entry.diameter_x = 15;
- touch_entry.diameter_y = 15;
- touch_entry.rotation_angle = 0;
+ touch_entry.diameter_x = Settings::values.touchscreen.diameter_x;
+ touch_entry.diameter_y = Settings::values.touchscreen.diameter_y;
+ touch_entry.rotation_angle = Settings::values.touchscreen.rotation_angle;
const u64 tick = CoreTiming::GetTicks();
touch_entry.delta_time = tick - last_touch;
last_touch = tick;
- touch_entry.finger = 0;
+ touch_entry.finger = Settings::values.touchscreen.finger;
cur_entry.entry_count = 1;
} else {
cur_entry.entry_count = 0;
@@ -60,6 +61,6 @@ void Controller_Touchscreen::OnUpdate(u8* data, std::size_t size) {
}
void Controller_Touchscreen::OnLoadInputDevices() {
- touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device);
+ touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index e5db6e6ba..94cd0eba9 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -4,6 +4,7 @@
#pragma once
+#include "common/bit_field.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -29,9 +30,18 @@ public:
void OnLoadInputDevices() override;
private:
+ struct Attributes {
+ union {
+ u32 raw{};
+ BitField<0, 1, u32_le> start_touch;
+ BitField<1, 1, u32_le> end_touch;
+ };
+ };
+ static_assert(sizeof(Attributes) == 0x4, "Attributes is an invalid size");
+
struct TouchState {
u64_le delta_time;
- u32_le attribute;
+ Attributes attribute;
u32_le finger;
u32_le x;
u32_le y;
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index a9aa9ec78..268409257 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -13,8 +13,9 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/shared_memory.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/hid/hid.h"
#include "core/hle/service/hid/irs.h"
#include "core/hle/service/hid/xcd.h"
@@ -34,8 +35,8 @@
namespace Service::HID {
// Updating period for each HID device.
-// TODO(shinyquagsire23): These need better values.
-constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
+// TODO(ogniK): Find actual polling rate of hid
+constexpr u64 pad_update_ticks = CoreTiming::BASE_CLOCK_RATE / 66;
constexpr u64 accelerometer_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr u64 gyroscope_update_ticks = CoreTiming::BASE_CLOCK_RATE / 100;
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
@@ -96,6 +97,8 @@ public:
// TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
+
+ ReloadInputDevices();
}
void ActivateController(HidController controller) {
@@ -122,10 +125,11 @@ public:
private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_mem);
- LOG_DEBUG(Service_HID, "called");
}
void UpdateControllers(u64 userdata, int cycles_late) {
@@ -161,9 +165,10 @@ public:
private:
void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_HID, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
@@ -284,10 +289,10 @@ public:
{519, nullptr, "GetPalmaOperationResult"},
{520, nullptr, "ReadPalmaPlayLog"},
{521, nullptr, "ResetPalmaPlayLog"},
- {522, nullptr, "SetIsPalmaAllConnectable"},
+ {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
{523, nullptr, "SetIsPalmaPairedConnectable"},
{524, nullptr, "PairPalma"},
- {525, nullptr, "SetPalmaBoostMode"},
+ {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
{1000, nullptr, "SetNpadCommunicationMode"},
{1001, nullptr, "GetNpadCommunicationMode"},
};
@@ -301,6 +306,11 @@ private:
std::shared_ptr<IAppletResource> applet_resource;
void CreateAppletResource(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
if (applet_resource == nullptr) {
applet_resource = std::make_shared<IAppletResource>();
}
@@ -308,206 +318,306 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAppletResource>(applet_resource);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateXpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto basic_xpad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
+ basic_xpad_id, applet_resource_user_id);
+
applet_resource->ActivateController(HidController::XPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
applet_resource->ActivateController(HidController::DebugPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
applet_resource->ActivateController(HidController::Touchscreen);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateMouse(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
applet_resource->ActivateController(HidController::Mouse);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
applet_resource->ActivateController(HidController::Keyboard);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateGesture(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
+ applet_resource_user_id);
+
applet_resource->ActivateController(HidController::Gesture);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
// Should have no effect with how our npad sets up the data
+ IPC::RequestParser rp{ctx};
+ const auto unknown{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
+ applet_resource_user_id);
+
applet_resource->ActivateController(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto handle = rp.PopRaw<u32>();
+ const auto handle{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
+ applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto handle{rp.Pop<u32>()};
+ const auto drift_mode{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, handle={}, drift_mode={}, applet_resource_user_id={}",
+ handle, drift_mode, applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto handle{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
+ applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
// TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
rb.Push(true);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto supported_styleset = rp.PopRaw<u32>();
+ const auto supported_styleset{rp.Pop<u32>()};
+
+ LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
+
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSupportedStyleSet({supported_styleset});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_DEBUG(Service_HID, "called");
}
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(controller.GetSupportedStyleSet().raw);
- LOG_DEBUG(Service_HID, "called");
}
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void ActivateNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
applet_resource->ActivateController(HidController::NPad);
- LOG_DEBUG(Service_HID, "called");
}
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto unknown{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
+ npad_id, applet_resource_user_id, unknown);
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetStyleSetChangedEvent());
- LOG_DEBUG(Service_HID, "called");
}
void DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
+ applet_resource_user_id);
+
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.DisconnectNPad(npad_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
+ const auto npad_id{rp.Pop<u32>()};
+
+ LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLedPattern(npad_id)
.raw);
- LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::RequestParser rp{ctx};
- const auto hold_type = rp.PopRaw<u64>();
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto hold_type{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
+ applet_resource_user_id, hold_type);
+
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
const auto& controller =
applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
- LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ npad_id, applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetVibrationEnabled(true);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetVibrationEnabled(false);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto controller_id = rp.PopRaw<u32>();
- const auto vibration_values = rp.PopRaw<Controller_NPad::Vibration>();
+ const auto controller_id{rp.Pop<u32>()};
+ const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}",
+ controller_id, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.VibrateController({controller_id}, {vibration_values});
- LOG_DEBUG(Service_HID, "called");
}
void SendVibrationValues(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
const auto controllers = ctx.ReadBuffer(0);
const auto vibrations = ctx.ReadBuffer(1);
@@ -525,74 +635,134 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto controller_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}",
+ controller_id, applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<Controller_NPad::Vibration>(
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.GetLastVibration());
- LOG_DEBUG(Service_HID, "called");
}
void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id = rp.PopRaw<u32>();
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
+ applet_resource_user_id);
+
auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_HID, "called");
}
void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown_1{rp.Pop<u32>()};
+ const auto unknown_2{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
+ unknown_1, unknown_2, applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto mode = rp.PopRaw<u32>();
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto mode{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
+ applet_resource_user_id, mode);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
rb.Push<u32>(0);
- LOG_DEBUG(Service_HID, "called");
}
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IActiveVibrationDeviceList>();
- LOG_DEBUG(Service_HID, "called");
}
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
+ applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto handle{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
+ applet_resource_user_id);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
void StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto handle{rp.Pop<u32>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, handle={}", handle);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto unknown{rp.Pop<u32>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, unknown={}",
+ applet_resource_user_id, unknown);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown{rp.Pop<u32>()};
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, unknown={}", unknown);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_HID, "(STUBBED) called");
}
};
diff --git a/src/core/hle/service/hid/irs.cpp b/src/core/hle/service/hid/irs.cpp
index 872e3c344..3c7f8b1ee 100644
--- a/src/core/hle/service/hid/irs.cpp
+++ b/src/core/hle/service/hid/irs.cpp
@@ -44,115 +44,133 @@ IRS::IRS() : ServiceFramework{"irs"} {
}
void IRS::ActivateIrsensor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::DeactivateIrsensor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetIrsensorSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_IRS, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(shared_mem);
- LOG_DEBUG(Service_IRS, "called");
}
void IRS::StopImageProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunMomentProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunClusteringProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunImageTransferProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetImageTransferProcessorState(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u64>(CoreTiming::GetTicks());
rb.PushRaw<u32>(0);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunTeraPluginProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::GetNpadIrCameraHandle(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(device_handle);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunPointingProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::SuspendImageProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::CheckFirmwareVersion(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::SetFunctionLevel(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunImageTransferExProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::RunIrLedProcessor(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::StopImageProcessorAsync(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
void IRS::ActivateIrsensorWithFunctionLevel(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_IRS, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_IRS, "(STUBBED) called");
}
IRS::~IRS() = default;
diff --git a/src/core/hle/service/lbl/lbl.cpp b/src/core/hle/service/lbl/lbl.cpp
index 164c57e18..e8f9f2d29 100644
--- a/src/core/hle/service/lbl/lbl.cpp
+++ b/src/core/hle/service/lbl/lbl.cpp
@@ -55,29 +55,29 @@ public:
private:
void EnableVrMode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
vr_mode_enabled = true;
-
- LOG_DEBUG(Service_LBL, "called");
}
void DisableVrMode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
vr_mode_enabled = false;
-
- LOG_DEBUG(Service_LBL, "called");
}
void IsVrModeEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LBL, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(vr_mode_enabled);
-
- LOG_DEBUG(Service_LBL, "called");
}
bool vr_mode_enabled = false;
diff --git a/src/core/hle/service/ldn/ldn.cpp b/src/core/hle/service/ldn/ldn.cpp
index 167f2c66a..e250595e3 100644
--- a/src/core/hle/service/ldn/ldn.cpp
+++ b/src/core/hle/service/ldn/ldn.cpp
@@ -44,11 +44,11 @@ public:
}
void CreateMonitorService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LDN, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IMonitorService>();
-
- LOG_DEBUG(Service_LDN, "called");
}
};
@@ -104,11 +104,11 @@ public:
}
void CreateSystemLocalCommunicationService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LDN, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILocalCommunicationService>("ISystemLocalCommunicationService");
-
- LOG_DEBUG(Service_LDN, "called");
}
};
@@ -125,11 +125,11 @@ public:
}
void CreateUserLocalCommunicationService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LDN, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILocalCommunicationService>("IUserLocalCommunicationService");
-
- LOG_DEBUG(Service_LDN, "called");
}
};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d607d985e..9df7ac50f 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -4,7 +4,10 @@
#include <memory>
#include <fmt/format.h>
+#include <mbedtls/sha256.h>
+#include "common/alignment.h"
+#include "common/hex_util.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/ldr/ldr.h"
@@ -13,6 +16,21 @@
namespace Service::LDR {
+constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
+constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
+constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
+constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
+constexpr ResultCode ERROR_MAXIMUM_NRO{ErrorModule::Loader, 55};
+constexpr ResultCode ERROR_MAXIMUM_NRR{ErrorModule::Loader, 56};
+constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
+constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
+constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
+constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
+constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
+constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
+
+constexpr u64 MAXIMUM_LOADED_RO = 0x40;
+
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
public:
explicit DebugMonitor() : ServiceFramework{"ldr:dmnt"} {
@@ -64,9 +82,9 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, &RelocatableObject::LoadNro, "LoadNro"},
- {1, nullptr, "UnloadNro"},
+ {1, &RelocatableObject::UnloadNro, "UnloadNro"},
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
- {3, nullptr, "UnloadNrr"},
+ {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
{4, &RelocatableObject::Initialize, "Initialize"},
};
// clang-format on
@@ -75,9 +93,126 @@ public:
}
void LoadNrr(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const VAddr nrr_addr{rp.Pop<VAddr>()};
+ const u64 nrr_size{rp.Pop<u64>()};
+ LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}, nrr_size={:016X}", nrr_addr,
+ nrr_size);
+
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ if (nrr.size() >= MAXIMUM_LOADED_RO) {
+ LOG_ERROR(Service_LDR, "Loading new NRR would exceed the maximum number of loaded NRRs "
+ "(0x40)! Failing...");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_MAXIMUM_NRR);
+ return;
+ }
+
+ // NRR Address does not fall on 0x1000 byte boundary
+ if (!Common::Is4KBAligned(nrr_addr)) {
+ LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ // NRR Size is zero or causes overflow
+ if (nrr_addr + nrr_size <= nrr_addr || nrr_size == 0 || !Common::Is4KBAligned(nrr_size)) {
+ LOG_ERROR(Service_LDR, "NRR Size is invalid! (nrr_address={:016X}, nrr_size={:016X})",
+ nrr_addr, nrr_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_SIZE);
+ return;
+ }
+ // Read NRR data from memory
+ std::vector<u8> nrr_data(nrr_size);
+ Memory::ReadBlock(nrr_addr, nrr_data.data(), nrr_size);
+ NRRHeader header;
+ std::memcpy(&header, nrr_data.data(), sizeof(NRRHeader));
+
+ if (header.magic != Common::MakeMagic('N', 'R', 'R', '0')) {
+ LOG_ERROR(Service_LDR, "NRR did not have magic 'NRR0' (actual {:08X})!", header.magic);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRR);
+ return;
+ }
+
+ if (header.size != nrr_size) {
+ LOG_ERROR(Service_LDR,
+ "NRR header reported size did not match LoadNrr parameter size! "
+ "(header_size={:016X}, loadnrr_size={:016X})",
+ header.size, nrr_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_SIZE);
+ return;
+ }
+
+ if (Core::CurrentProcess()->GetTitleID() != header.title_id) {
+ LOG_ERROR(Service_LDR,
+ "Attempting to load NRR with title ID other than current process. (actual "
+ "{:016X})!",
+ header.title_id);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRR);
+ return;
+ }
+
+ std::vector<SHA256Hash> hashes;
+
+ // Copy all hashes in the NRR (specified by hash count/hash offset) into vector.
+ for (std::size_t i = header.hash_offset;
+ i < (header.hash_offset + (header.hash_count * sizeof(SHA256Hash))); i += 8) {
+ SHA256Hash hash;
+ std::memcpy(hash.data(), nrr_data.data() + i, sizeof(SHA256Hash));
+ hashes.emplace_back(hash);
+ }
+
+ nrr.insert_or_assign(nrr_addr, std::move(hashes));
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void UnloadNrr(Kernel::HLERequestContext& ctx) {
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const auto nrr_addr{rp.Pop<VAddr>()};
+ LOG_DEBUG(Service_LDR, "called with nrr_addr={:016X}", nrr_addr);
+
+ if (!Common::Is4KBAligned(nrr_addr)) {
+ LOG_ERROR(Service_LDR, "NRR Address has invalid alignment (actual {:016X})!", nrr_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ const auto iter = nrr.find(nrr_addr);
+ if (iter == nrr.end()) {
+ LOG_ERROR(Service_LDR,
+ "Attempting to unload NRR which has not been loaded! (addr={:016X})",
+ nrr_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRR_ADDRESS);
+ return;
+ }
+
+ nrr.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_LDR, "(STUBBED) called");
}
void LoadNro(Kernel::HLERequestContext& ctx) {
@@ -87,33 +222,260 @@ public:
const u64 nro_size{rp.Pop<u64>()};
const VAddr bss_addr{rp.Pop<VAddr>()};
const u64 bss_size{rp.Pop<u64>()};
+ LOG_DEBUG(
+ Service_LDR,
+ "called with nro_addr={:016X}, nro_size={:016X}, bss_addr={:016X}, bss_size={:016X}",
+ nro_addr, nro_size, bss_addr, bss_size);
+
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ if (nro.size() >= MAXIMUM_LOADED_RO) {
+ LOG_ERROR(Service_LDR, "Loading new NRO would exceed the maximum number of loaded NROs "
+ "(0x40)! Failing...");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_MAXIMUM_NRO);
+ return;
+ }
+
+ // NRO Address does not fall on 0x1000 byte boundary
+ if (!Common::Is4KBAligned(nro_addr)) {
+ LOG_ERROR(Service_LDR, "NRO Address has invalid alignment (actual {:016X})!", nro_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ // NRO Size or BSS Size is zero or causes overflow
+ const auto nro_size_valid =
+ nro_size != 0 && nro_addr + nro_size > nro_addr && Common::Is4KBAligned(nro_size);
+ const auto bss_size_valid =
+ nro_size + bss_size >= nro_size && (bss_size == 0 || bss_addr + bss_size > bss_addr);
+
+ if (!nro_size_valid || !bss_size_valid) {
+ LOG_ERROR(Service_LDR,
+ "NRO Size or BSS Size is invalid! (nro_address={:016X}, nro_size={:016X}, "
+ "bss_address={:016X}, bss_size={:016X})",
+ nro_addr, nro_size, bss_addr, bss_size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_SIZE);
+ return;
+ }
// Read NRO data from memory
std::vector<u8> nro_data(nro_size);
Memory::ReadBlock(nro_addr, nro_data.data(), nro_size);
+ SHA256Hash hash{};
+ mbedtls_sha256(nro_data.data(), nro_data.size(), hash.data(), 0);
+
+ // NRO Hash is already loaded
+ if (std::any_of(nro.begin(), nro.end(), [&hash](const std::pair<VAddr, NROInfo>& info) {
+ return info.second.hash == hash;
+ })) {
+ LOG_ERROR(Service_LDR, "NRO is already loaded!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_ALREADY_LOADED);
+ return;
+ }
+
+ // NRO Hash is not in any loaded NRR
+ if (!IsValidNROHash(hash)) {
+ LOG_ERROR(Service_LDR,
+ "NRO hash is not present in any currently loaded NRRs (hash={})!",
+ Common::HexArrayToString(hash));
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_MISSING_NRR_HASH);
+ return;
+ }
+
+ NROHeader header;
+ std::memcpy(&header, nro_data.data(), sizeof(NROHeader));
+
+ if (!IsValidNRO(header, nro_size, bss_size)) {
+ LOG_ERROR(Service_LDR, "NRO was invalid!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRO);
+ return;
+ }
+
// Load NRO as new executable module
- const VAddr addr{*Core::CurrentProcess()->VMManager().FindFreeRegion(nro_size + bss_size)};
- Loader::AppLoader_NRO::LoadNro(nro_data, fmt::format("nro-{:08x}", addr), addr);
+ auto* process = Core::CurrentProcess();
+ auto& vm_manager = process->VMManager();
+ auto map_address = vm_manager.FindFreeRegion(nro_size + bss_size);
+
+ if (!map_address.Succeeded() ||
+ *map_address + nro_size + bss_size > vm_manager.GetAddressSpaceEndAddress()) {
+
+ LOG_ERROR(Service_LDR,
+ "General error while allocation memory or no available memory to allocate!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_MEMORY_STATE);
+ return;
+ }
+
+ ASSERT(vm_manager
+ .MirrorMemory(*map_address, nro_addr, nro_size,
+ Kernel::MemoryState::ModuleCodeStatic)
+ .IsSuccess());
+ ASSERT(vm_manager.UnmapRange(nro_addr, nro_size).IsSuccess());
+
+ if (bss_size > 0) {
+ ASSERT(vm_manager
+ .MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
+ Kernel::MemoryState::ModuleCodeStatic)
+ .IsSuccess());
+ ASSERT(vm_manager.UnmapRange(bss_addr, bss_size).IsSuccess());
+ }
- // TODO(bunnei): This is an incomplete implementation. It was tested with Super Mario Party.
- // It is currently missing:
- // - Signature checks with LoadNRR
- // - Checking if a module has already been loaded
- // - Using/validating BSS, etc. params (these are used from NRO header instead)
- // - Error checking
- // - ...Probably other things
+ vm_manager.ReprotectRange(*map_address, header.text_size,
+ Kernel::VMAPermission::ReadExecute);
+ vm_manager.ReprotectRange(*map_address + header.ro_offset, header.ro_size,
+ Kernel::VMAPermission::Read);
+ vm_manager.ReprotectRange(*map_address + header.rw_offset, header.rw_size,
+ Kernel::VMAPermission::ReadWrite);
+
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
+
+ nro.insert_or_assign(*map_address, NROInfo{hash, nro_size + bss_size});
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(addr);
- LOG_WARNING(Service_LDR, "(STUBBED) called");
+ rb.Push(*map_address);
}
- void Initialize(Kernel::HLERequestContext& ctx) {
+ void UnloadNro(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ rp.Skip(2, false);
+ const VAddr mapped_addr{rp.PopRaw<VAddr>()};
+ const VAddr heap_addr{rp.PopRaw<VAddr>()};
+ LOG_DEBUG(Service_LDR, "called with mapped_addr={:016X}, heap_addr={:016X}", mapped_addr,
+ heap_addr);
+
+ if (!initialized) {
+ LOG_ERROR(Service_LDR, "LDR:RO not initialized before use!");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_NOT_INITIALIZED);
+ return;
+ }
+
+ if (!Common::Is4KBAligned(mapped_addr) || !Common::Is4KBAligned(heap_addr)) {
+ LOG_ERROR(Service_LDR,
+ "NRO/BSS Address has invalid alignment (actual nro_addr={:016X}, "
+ "bss_addr={:016X})!",
+ mapped_addr, heap_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_ALIGNMENT);
+ return;
+ }
+
+ const auto iter = nro.find(mapped_addr);
+ if (iter == nro.end()) {
+ LOG_ERROR(Service_LDR,
+ "The NRO attempting to unmap was not mapped or has an invalid address "
+ "(actual {:016X})!",
+ mapped_addr);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERROR_INVALID_NRO_ADDRESS);
+ return;
+ }
+
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
+ const auto& nro_size = iter->second.size;
+
+ ASSERT(vm_manager
+ .MirrorMemory(heap_addr, mapped_addr, nro_size,
+ Kernel::MemoryState::ModuleCodeStatic)
+ .IsSuccess());
+ ASSERT(vm_manager.UnmapRange(mapped_addr, nro_size).IsSuccess());
+
+ Core::System::GetInstance().InvalidateCpuInstructionCaches();
+
+ nro.erase(iter);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
+ }
+
+ void Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_LDR, "(STUBBED) called");
+
+ initialized = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+private:
+ using SHA256Hash = std::array<u8, 0x20>;
+
+ struct NROHeader {
+ INSERT_PADDING_WORDS(1);
+ u32_le mod_offset;
+ INSERT_PADDING_WORDS(2);
+ u32_le magic;
+ u32_le version;
+ u32_le nro_size;
+ u32_le flags;
+ u32_le text_offset;
+ u32_le text_size;
+ u32_le ro_offset;
+ u32_le ro_size;
+ u32_le rw_offset;
+ u32_le rw_size;
+ u32_le bss_size;
+ INSERT_PADDING_WORDS(1);
+ std::array<u8, 0x20> build_id;
+ INSERT_PADDING_BYTES(0x20);
+ };
+ static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size.");
+
+ struct NRRHeader {
+ u32_le magic;
+ INSERT_PADDING_BYTES(12);
+ u64_le title_id_mask;
+ u64_le title_id_pattern;
+ INSERT_PADDING_BYTES(16);
+ std::array<u8, 0x100> modulus;
+ std::array<u8, 0x100> signature_1;
+ std::array<u8, 0x100> signature_2;
+ u64_le title_id;
+ u32_le size;
+ INSERT_PADDING_BYTES(4);
+ u32_le hash_offset;
+ u32_le hash_count;
+ INSERT_PADDING_BYTES(8);
+ };
+ static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size.");
+
+ struct NROInfo {
+ SHA256Hash hash;
+ u64 size;
+ };
+
+ bool initialized = false;
+
+ std::map<VAddr, NROInfo> nro;
+ std::map<VAddr, std::vector<SHA256Hash>> nrr;
+
+ bool IsValidNROHash(const SHA256Hash& hash) {
+ return std::any_of(
+ nrr.begin(), nrr.end(), [&hash](const std::pair<VAddr, std::vector<SHA256Hash>>& p) {
+ return std::find(p.second.begin(), p.second.end(), hash) != p.second.end();
+ });
+ }
+
+ static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) {
+ return header.magic == Common::MakeMagic('N', 'R', 'O', '0') &&
+ header.nro_size == nro_size && header.bss_size == bss_size &&
+ header.ro_offset == header.text_offset + header.text_size &&
+ header.rw_offset == header.ro_offset + header.ro_size &&
+ nro_size == header.rw_offset + header.rw_size &&
+ Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) &&
+ Common::Is4KBAligned(header.rw_size);
}
};
diff --git a/src/core/hle/service/lm/lm.cpp b/src/core/hle/service/lm/lm.cpp
index c89157a4d..1f462e087 100644
--- a/src/core/hle/service/lm/lm.cpp
+++ b/src/core/hle/service/lm/lm.cpp
@@ -18,7 +18,7 @@ public:
ILogger() : ServiceFramework("ILogger") {
static const FunctionInfo functions[] = {
{0x00000000, &ILogger::Initialize, "Initialize"},
- {0x00000001, nullptr, "SetDestination"},
+ {0x00000001, &ILogger::SetDestination, "SetDestination"},
};
RegisterHandlers(functions);
}
@@ -178,6 +178,17 @@ private:
}
}
+ // This service function is intended to be used as a way to
+ // redirect logging output to different destinations, however,
+ // given we always want to see the logging output, it's sufficient
+ // to do nothing and return success here.
+ void SetDestination(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LM, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
std::ostringstream log_stream;
};
@@ -198,11 +209,11 @@ public:
* 0: ResultCode
*/
void OpenLogger(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_LM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ILogger>();
-
- LOG_DEBUG(Service_LM, "called");
}
};
diff --git a/src/core/hle/service/mm/mm_u.cpp b/src/core/hle/service/mm/mm_u.cpp
index e1f17a926..def63dc8a 100644
--- a/src/core/hle/service/mm/mm_u.cpp
+++ b/src/core/hle/service/mm/mm_u.cpp
@@ -31,12 +31,14 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Finalize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -45,15 +47,16 @@ private:
IPC::RequestParser rp{ctx};
min = rp.Pop<u32>();
max = rp.Pop<u32>();
- current = min;
-
LOG_WARNING(Service_MM, "(STUBBED) called, min=0x{:X}, max=0x{:X}", min, max);
+
+ current = min;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Get(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(current);
@@ -61,6 +64,7 @@ private:
void InitializeWithId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(id); // Any non zero value
@@ -68,6 +72,7 @@ private:
void FinalizeWithId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -77,16 +82,17 @@ private:
u32 input_id = rp.Pop<u32>();
min = rp.Pop<u32>();
max = rp.Pop<u32>();
- current = min;
-
LOG_WARNING(Service_MM, "(STUBBED) called, input_id=0x{:X}, min=0x{:X}, max=0x{:X}",
input_id, min, max);
+
+ current = min;
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetWithId(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_MM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push(current);
diff --git a/src/core/hle/service/nfc/nfc.cpp b/src/core/hle/service/nfc/nfc.cpp
index 30e542542..5c62d42ba 100644
--- a/src/core/hle/service/nfc/nfc.cpp
+++ b/src/core/hle/service/nfc/nfc.cpp
@@ -43,11 +43,11 @@ public:
private:
void CreateAmInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IAm>();
-
- LOG_DEBUG(Service_NFC, "called");
}
};
@@ -91,11 +91,11 @@ public:
private:
void CreateUserInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<MFIUser>();
-
- LOG_DEBUG(Service_NFC, "called");
}
};
@@ -138,19 +138,19 @@ private:
};
void InitializeOld(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(RESULT_SUCCESS);
-
// We don't deal with hardware initialization so we can just stub this.
- LOG_DEBUG(Service_NFC, "called");
}
void IsNfcEnabledOld(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u8>(Settings::values.enable_nfc);
-
- LOG_DEBUG(Service_NFC, "IsNfcEnabledOld");
}
void GetStateOld(Kernel::HLERequestContext& ctx) {
@@ -183,11 +183,11 @@ public:
private:
void CreateUserInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IUser>();
-
- LOG_DEBUG(Service_NFC, "called");
}
};
@@ -241,11 +241,11 @@ public:
private:
void CreateSystemInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystem>();
-
- LOG_DEBUG(Service_NFC, "called");
}
};
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index c1af878fe..2254fb46b 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -7,9 +7,11 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/lock.h"
-#include "core/hle/service/hid/hid.h"
#include "core/hle/service/nfp/nfp.h"
#include "core/hle/service/nfp/nfp_user.h"
@@ -23,8 +25,8 @@ constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {
auto& kernel = Core::System::GetInstance().Kernel();
- nfc_tag_load =
- Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:NFCTagDetected");
+ nfc_tag_load = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "IUser:NFCTagDetected");
}
Module::Interface::~Interface() = default;
@@ -63,10 +65,10 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- deactivate_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
- availability_change_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
- "IUser:AvailabilityChangeEvent");
+ deactivate_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot, "IUser:DeactivateEvent");
+ availability_change_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot, "IUser:AvailabilityChangeEvent");
}
private:
@@ -108,30 +110,29 @@ private:
static_assert(sizeof(CommonInfo) == 0x40, "CommonInfo is an invalid size");
void Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0};
rb.Push(RESULT_SUCCESS);
state = State::Initialized;
-
- LOG_DEBUG(Service_NFC, "called");
}
void GetState(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NFC, "called");
+
IPC::ResponseBuilder rb{ctx, 3, 0};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(static_cast<u32>(state));
-
- LOG_DEBUG(Service_NFC, "called");
}
void ListDevices(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 array_size = rp.Pop<u32>();
+ LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
ctx.WriteBuffer(&device_handle, sizeof(device_handle));
- LOG_DEBUG(Service_NFP, "called, array_size={}", array_size);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1);
@@ -141,6 +142,7 @@ private:
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(npad_id);
@@ -150,6 +152,7 @@ private:
IPC::RequestParser rp{ctx};
const u64 dev_handle = rp.Pop<u64>();
LOG_DEBUG(Service_NFP, "called, dev_handle=0x{:X}", dev_handle);
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(nfp_interface.GetNFCEvent());
@@ -163,15 +166,16 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(deactivate_event);
+ rb.PushCopyObjects(deactivate_event.readable);
}
void StopDetection(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
+
switch (device_state) {
case DeviceState::TagFound:
case DeviceState::TagNearby:
- deactivate_event->Signal();
+ deactivate_event.writable->Signal();
device_state = DeviceState::Initialized;
break;
case DeviceState::SearchingForTag:
@@ -185,6 +189,7 @@ private:
void GetDeviceState(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
+
auto nfc_event = nfp_interface.GetNFCEvent();
if (!nfc_event->ShouldWait(Kernel::GetCurrentThread()) && !has_attached_handle) {
device_state = DeviceState::TagFound;
@@ -212,7 +217,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
auto amiibo = nfp_interface.GetAmiiboBuffer();
TagInfo tag_info{};
- std::memcpy(tag_info.uuid.data(), amiibo.uuid.data(), sizeof(tag_info.uuid.size()));
+ tag_info.uuid = amiibo.uuid;
tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
@@ -261,7 +266,7 @@ private:
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(availability_change_event);
+ rb.PushCopyObjects(availability_change_event.readable);
}
void GetRegisterInfo(Kernel::HLERequestContext& ctx) {
@@ -312,17 +317,18 @@ private:
}
bool has_attached_handle{};
- const u64 device_handle{Common::MakeMagic('Y', 'U', 'Z', 'U')};
- const u32 npad_id{0}; // Player 1 controller
+ const u64 device_handle{0}; // Npad device 1
+ const u32 npad_id{0}; // Player 1 controller
State state{State::NonInitialized};
DeviceState device_state{DeviceState::Initialized};
- Kernel::SharedPtr<Kernel::Event> deactivate_event;
- Kernel::SharedPtr<Kernel::Event> availability_change_event;
+ Kernel::EventPair deactivate_event;
+ Kernel::EventPair availability_change_event;
const Module::Interface& nfp_interface;
};
void Module::Interface::CreateUserInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NFP, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IUser>(*this);
@@ -335,12 +341,14 @@ bool Module::Interface::LoadAmiibo(const std::vector<u8>& buffer) {
}
std::memcpy(&amiibo, buffer.data(), sizeof(amiibo));
- nfc_tag_load->Signal();
+ nfc_tag_load.writable->Signal();
return true;
}
-const Kernel::SharedPtr<Kernel::Event>& Module::Interface::GetNFCEvent() const {
- return nfc_tag_load;
+
+const Kernel::SharedPtr<Kernel::ReadableEvent>& Module::Interface::GetNFCEvent() const {
+ return nfc_tag_load.readable;
}
+
const Module::Interface::AmiiboFile& Module::Interface::GetAmiiboBuffer() const {
return amiibo;
}
diff --git a/src/core/hle/service/nfp/nfp.h b/src/core/hle/service/nfp/nfp.h
index 5c0ae8a54..a1817e991 100644
--- a/src/core/hle/service/nfp/nfp.h
+++ b/src/core/hle/service/nfp/nfp.h
@@ -6,7 +6,8 @@
#include <array>
#include <vector>
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/service.h"
namespace Service::NFP {
@@ -33,11 +34,11 @@ public:
void CreateUserInterface(Kernel::HLERequestContext& ctx);
bool LoadAmiibo(const std::vector<u8>& buffer);
- const Kernel::SharedPtr<Kernel::Event>& GetNFCEvent() const;
+ const Kernel::SharedPtr<Kernel::ReadableEvent>& GetNFCEvent() const;
const AmiiboFile& GetAmiiboBuffer() const;
private:
- Kernel::SharedPtr<Kernel::Event> nfc_tag_load{};
+ Kernel::EventPair nfc_tag_load{};
AmiiboFile amiibo{};
protected:
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 75dcd94a3..60479bb45 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -4,7 +4,9 @@
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h"
@@ -56,19 +58,23 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- event1 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event1");
- event2 = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "IRequest:Event2");
+ event1 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "IRequest:Event1");
+ event2 = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "IRequest:Event2");
}
private:
void Submit(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetRequestState(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
@@ -76,30 +82,34 @@ private:
void GetResult(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetSystemEventReadableHandles(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2, 2};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(event1, event2);
+ rb.PushCopyObjects(event1.readable, event2.readable);
}
void Cancel(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void SetConnectionConfirmationOption(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
- Kernel::SharedPtr<Kernel::Event> event1, event2;
+ Kernel::EventPair event1, event2;
};
class INetworkProfile final : public ServiceFramework<INetworkProfile> {
@@ -122,32 +132,36 @@ private:
void GetClientId(Kernel::HLERequestContext& ctx) {
static constexpr u32 client_id = 1;
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(client_id); // Client ID needs to be non zero otherwise it's considered invalid
}
void CreateScanRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IScanRequest>();
-
- LOG_DEBUG(Service_NIFM, "called");
}
void CreateRequest(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IRequest>();
-
- LOG_DEBUG(Service_NIFM, "called");
}
void RemoveNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
ASSERT_MSG(ctx.GetReadBufferSize() == 0x17c, "NetworkProfileData is not the correct size");
u128 uuid{};
auto buffer = ctx.ReadBuffer();
@@ -158,23 +172,24 @@ private:
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<INetworkProfile>();
rb.PushRaw<u128>(uuid);
-
- LOG_DEBUG(Service_NIFM, "called");
}
void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
}
void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
}
void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u8>(0);
@@ -235,17 +250,19 @@ public:
}
void CreateGeneralServiceOld(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
}
void CreateGeneralService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIFM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IGeneralService>();
- LOG_DEBUG(Service_NIFM, "called");
}
};
diff --git a/src/core/hle/service/nim/nim.cpp b/src/core/hle/service/nim/nim.cpp
index 18091c9bb..0dabcd23b 100644
--- a/src/core/hle/service/nim/nim.cpp
+++ b/src/core/hle/service/nim/nim.cpp
@@ -6,7 +6,9 @@
#include <ctime>
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nim/nim.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
@@ -138,57 +140,61 @@ public:
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- finished_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::OneShot,
- "IEnsureNetworkClockAvailabilityService:FinishEvent");
+ finished_event = Kernel::WritableEvent::CreateEventPair(
+ kernel, Kernel::ResetType::OneShot,
+ "IEnsureNetworkClockAvailabilityService:FinishEvent");
}
private:
- Kernel::SharedPtr<Kernel::Event> finished_event;
+ Kernel::EventPair finished_event;
void StartTask(Kernel::HLERequestContext& ctx) {
// No need to connect to the internet, just finish the task straight away.
- finished_event->Signal();
+ LOG_DEBUG(Service_NIM, "called");
+ finished_event.writable->Signal();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_NIM, "called");
}
void GetFinishNotificationEvent(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(finished_event);
- LOG_DEBUG(Service_NIM, "called");
+ rb.PushCopyObjects(finished_event.readable);
}
void GetResult(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIM, "called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_NIM, "called");
}
void Cancel(Kernel::HLERequestContext& ctx) {
- finished_event->Clear();
+ LOG_DEBUG(Service_NIM, "called");
+ finished_event.writable->Clear();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_NIM, "called");
}
void IsProcessing(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIM, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<u32>(0); // We instantly process the request
- LOG_DEBUG(Service_NIM, "called");
}
void GetServerTime(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIM, "called");
+
const s64 server_time{std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count()};
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.PushRaw<s64>(server_time);
- LOG_DEBUG(Service_NIM, "called");
}
};
@@ -208,23 +214,26 @@ public:
private:
void OpenEnsureNetworkClockAvailabilityService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NIM, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IEnsureNetworkClockAvailabilityService>();
- LOG_DEBUG(Service_NIM, "called");
}
// TODO(ogniK): Do we need these?
void SuspendAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_NIM, "(STUBBED) called");
}
void ResumeAutonomicTimeCorrection(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIM, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_WARNING(Service_NIM, "(STUBBED) called");
}
};
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 07c1381fe..2663f56b1 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -2,6 +2,9 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/patch_manager.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/ns/ns.h"
@@ -118,7 +121,7 @@ public:
{305, nullptr, "TerminateSystemApplet"},
{306, nullptr, "LaunchOverlayApplet"},
{307, nullptr, "TerminateOverlayApplet"},
- {400, nullptr, "GetApplicationControlData"},
+ {400, &IApplicationManagerInterface::GetApplicationControlData, "GetApplicationControlData"},
{401, nullptr, "InvalidateAllApplicationControlCache"},
{402, nullptr, "RequestDownloadApplicationControlData"},
{403, nullptr, "GetMaxApplicationControlCacheCount"},
@@ -243,6 +246,65 @@ public:
RegisterHandlers(functions);
}
+
+ void GetApplicationControlData(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto flag = rp.PopRaw<u64>();
+ LOG_DEBUG(Service_NS, "called with flag={:016X}", flag);
+
+ const auto title_id = rp.PopRaw<u64>();
+
+ const auto size = ctx.GetWriteBufferSize();
+
+ const FileSys::PatchManager pm{title_id};
+ const auto control = pm.GetControlMetadata();
+
+ std::vector<u8> out;
+
+ if (control.first != nullptr) {
+ if (size < 0x4000) {
+ LOG_ERROR(Service_NS,
+ "output buffer is too small! (actual={:016X}, expected_min=0x4000)",
+ size);
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code for this.
+ rb.Push(ResultCode(-1));
+ return;
+ }
+
+ out.resize(0x4000);
+ const auto bytes = control.first->GetRawBytes();
+ std::memcpy(out.data(), bytes.data(), bytes.size());
+ } else {
+ LOG_WARNING(Service_NS, "missing NACP data for title_id={:016X}, defaulting to zeros.",
+ title_id);
+ out.resize(std::min<u64>(0x4000, size));
+ }
+
+ if (control.second != nullptr) {
+ if (size < 0x4000 + control.second->GetSize()) {
+ LOG_ERROR(Service_NS,
+ "output buffer is too small! (actual={:016X}, expected_min={:016X})",
+ size, 0x4000 + control.second->GetSize());
+ IPC::ResponseBuilder rb{ctx, 2};
+ // TODO(DarkLordZach): Find a better error code for this.
+ rb.Push(ResultCode(-1));
+ return;
+ }
+
+ out.resize(0x4000 + control.second->GetSize());
+ control.second->Read(out.data() + 0x4000, control.second->GetSize());
+ } else {
+ LOG_WARNING(Service_NS, "missing icon data for title_id={:016X}, defaulting to zeros.",
+ title_id);
+ }
+
+ ctx.WriteBuffer(out);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(static_cast<u32>(out.size()));
+ }
};
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
@@ -371,11 +433,11 @@ public:
private:
template <typename T>
void PushInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NS, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<T>();
-
- LOG_DEBUG(Service_NS, "called");
}
};
@@ -464,11 +526,11 @@ public:
private:
void OpenSystemUpdateControl(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NS, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemUpdateControl>();
-
- LOG_DEBUG(Service_NS, "called");
}
};
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 44accecb7..ad176f89d 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -281,6 +281,7 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
const u32 shared_font_type{rp.Pop<u32>()};
// Games don't call this so all fonts should be loaded
LOG_DEBUG(Service_NS, "called, shared_font_type={}", shared_font_type);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -288,8 +289,8 @@ void PL_U::RequestLoad(Kernel::HLERequestContext& ctx) {
void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
-
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(LoadState::Done));
@@ -298,8 +299,8 @@ void PL_U::GetLoadState(Kernel::HLERequestContext& ctx) {
void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
-
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(impl->GetSharedFontRegion(font_id).size);
@@ -308,8 +309,8 @@ void PL_U::GetSize(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u32 font_id{rp.Pop<u32>()};
-
LOG_DEBUG(Service_NS, "called, font_id={}", font_id);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(impl->GetSharedFontRegion(font_id).offset);
@@ -317,6 +318,7 @@ void PL_U::GetSharedMemoryAddressOffset(Kernel::HLERequestContext& ctx) {
void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
// Map backing memory for the font data
+ LOG_DEBUG(Service_NS, "called");
Core::CurrentProcess()->VMManager().MapMemoryBlock(SHARED_FONT_MEM_VADDR, impl->shared_font, 0,
SHARED_FONT_MEM_SIZE,
Kernel::MemoryState::Shared);
@@ -328,7 +330,6 @@ void PL_U::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& ctx) {
Kernel::MemoryPermission::Read, SHARED_FONT_MEM_VADDR, Kernel::MemoryRegion::BASE,
"PL_U:shared_font_mem");
- LOG_DEBUG(Service_NS, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(impl->shared_font_mem);
@@ -338,6 +339,7 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 language_code{rp.Pop<u64>()}; // TODO(ogniK): Find out what this is used for
LOG_DEBUG(Service_NS, "called, language_code={:X}", language_code);
+
IPC::ResponseBuilder rb{ctx, 4};
std::vector<u32> font_codes;
std::vector<u32> font_offsets;
@@ -351,6 +353,14 @@ void PL_U::GetSharedFontInOrderOfPriority(Kernel::HLERequestContext& ctx) {
font_sizes.push_back(region.size);
}
+ // Resize buffers if game requests smaller size output.
+ font_codes.resize(
+ std::min<std::size_t>(font_codes.size(), ctx.GetWriteBufferSize(0) / sizeof(u32)));
+ font_offsets.resize(
+ std::min<std::size_t>(font_offsets.size(), ctx.GetWriteBufferSize(1) / sizeof(u32)));
+ font_sizes.resize(
+ std::min<std::size_t>(font_sizes.size(), ctx.GetWriteBufferSize(2) / sizeof(u32)));
+
ctx.WriteBuffer(font_codes, 0);
ctx.WriteBuffer(font_offsets, 1);
ctx.WriteBuffer(font_sizes, 2);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
index c41ef7058..466db7ccd 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_as_gpu.cpp
@@ -54,6 +54,7 @@ u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& ou
IoctlInitalizeEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
+
return 0;
}
@@ -191,6 +192,7 @@ u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& ou
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
+
channel = params.fd;
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
index 7a88ae029..d57a54ee8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp
@@ -5,6 +5,8 @@
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core_timing.h"
+#include "core/core_timing_util.h"
#include "core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h"
namespace Service::Nvidia::Devices {
@@ -33,6 +35,8 @@ u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input, std::vec
return ZBCQueryTable(input, output);
case IoctlCommand::IocFlushL2:
return FlushL2(input, output);
+ case IoctlCommand::IocGetGpuTime:
+ return GetGpuTime(input, output);
}
UNIMPLEMENTED_MSG("Unimplemented ioctl");
return 0;
@@ -99,6 +103,7 @@ u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>&
u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
+
IoctlActiveSlotMask params{};
if (input.size() > 0) {
std::memcpy(&params, input.data(), input.size());
@@ -111,6 +116,7 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
+
IoctlZcullGetCtxSize params{};
if (input.size() > 0) {
std::memcpy(&params, input.data(), input.size());
@@ -122,6 +128,7 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
+
IoctlNvgpuGpuZcullGetInfoArgs params{};
if (input.size() > 0) {
@@ -144,6 +151,7 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
IoctlZbcSetTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO(ogniK): What does this even actually do?
@@ -153,6 +161,7 @@ u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>&
u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
IoctlZbcQueryTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
@@ -162,6 +171,7 @@ u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>
u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
IoctlFlushL2 params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
@@ -169,4 +179,14 @@ u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& outp
return 0;
}
+u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
+ LOG_DEBUG(Service_NVDRV, "called");
+
+ IoctlGetGpuTime params{};
+ std::memcpy(&params, input.data(), input.size());
+ params.gpu_time = CoreTiming::cyclesToNs(CoreTiming::GetTicks());
+ std::memcpy(output.data(), &params, output.size());
+ return 0;
+}
+
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
index 3bbf028ad..240435eea 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.h
@@ -156,6 +156,11 @@ private:
};
static_assert(sizeof(IoctlFlushL2) == 8, "IoctlFlushL2 is incorrect size");
+ struct IoctlGetGpuTime {
+ u64_le gpu_time;
+ };
+ static_assert(sizeof(IoctlGetGpuTime) == 8, "IoctlGetGpuTime is incorrect size");
+
u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
@@ -164,6 +169,7 @@ private:
u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
+ u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 874d5e1c3..0a650f36c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -8,7 +8,6 @@
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
#include "core/memory.h"
-#include "video_core/command_processor.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
@@ -61,12 +60,14 @@ u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+
nvmap_fd = params.nvmap_fd;
return 0;
}
u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
+
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
user_data = params.data;
@@ -75,6 +76,7 @@ u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& out
u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
+
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
params.data = user_data;
@@ -86,6 +88,7 @@ u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output)
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
zcull_params.mode);
+
std::memcpy(output.data(), &zcull_params, output.size());
return 0;
}
@@ -95,6 +98,7 @@ u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>&
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
params.size, params.mem);
+
std::memcpy(output.data(), &params, output.size());
return 0;
}
@@ -102,6 +106,7 @@ u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>&
u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&channel_priority, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
+
return 0;
}
@@ -113,6 +118,7 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
"unk1={:X}, unk2={:X}, unk3={:X}",
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
params.unk3);
+
params.fence_out.id = 0;
params.fence_out.value = 0;
std::memcpy(output.data(), &params, output.size());
@@ -124,11 +130,22 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
params.flags);
+
params.obj_id = 0x0;
std::memcpy(output.data(), &params, output.size());
return 0;
}
+static void PushGPUEntries(Tegra::CommandList&& entries) {
+ if (entries.empty()) {
+ return;
+ }
+
+ auto& dma_pusher{Core::System::GetInstance().GPU().DmaPusher()};
+ dma_pusher.Push(std::move(entries));
+ dma_pusher.DispatchCalls();
+}
+
u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
@@ -142,11 +159,11 @@ u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& outp
params.num_entries * sizeof(Tegra::CommandListHeader),
"Incorrect input size");
- std::vector<Tegra::CommandListHeader> entries(params.num_entries);
+ Tegra::CommandList entries(params.num_entries);
std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
params.num_entries * sizeof(Tegra::CommandListHeader));
- Core::System::GetInstance().GPU().ProcessCommandLists(entries);
+ PushGPUEntries(std::move(entries));
params.fence_out.id = 0;
params.fence_out.value = 0;
@@ -163,11 +180,11 @@ u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output)
LOG_WARNING(Service_NVDRV, "(STUBBED) called, gpfifo={:X}, num_entries={:X}, flags={:X}",
params.address, params.num_entries, params.flags);
- std::vector<Tegra::CommandListHeader> entries(params.num_entries);
+ Tegra::CommandList entries(params.num_entries);
Memory::ReadBlock(params.address, entries.data(),
params.num_entries * sizeof(Tegra::CommandListHeader));
- Core::System::GetInstance().GPU().ProcessCommandLists(entries);
+ PushGPUEntries(std::move(entries));
params.fence_out.id = 0;
params.fence_out.value = 0;
@@ -179,6 +196,7 @@ u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& outpu
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
+
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, output.size());
return 0;
@@ -188,6 +206,7 @@ u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>&
IoctlChannelSetTimeout params{};
std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
+
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 46dbbc37c..f5e8ea7c3 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -30,6 +30,7 @@ u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& outp
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+
nvmap_fd = params.nvmap_fd;
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
index c67f934f6..3e0951ab0 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvjpg.cpp
@@ -30,6 +30,7 @@ u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& outp
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+
nvmap_fd = params.nvmap_fd;
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index 727b9fee4..d544f0f31 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -30,6 +30,7 @@ u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+
nvmap_fd = params.nvmap_fd;
return 0;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvmap.cpp b/src/core/hle/service/nvdrv/devices/nvmap.cpp
index 43651d8a6..1ec796fc6 100644
--- a/src/core/hle/service/nvdrv/devices/nvmap.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvmap.cpp
@@ -54,6 +54,7 @@ u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
if (!params.size) {
+ LOG_ERROR(Service_NVDRV, "Size is 0");
return static_cast<u32>(NvErrCodes::InvalidValue);
}
// Create a new nvmap object and obtain a handle to it.
@@ -78,10 +79,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
if (!params.handle) {
+ LOG_ERROR(Service_NVDRV, "Handle is 0");
return static_cast<u32>(NvErrCodes::InvalidValue);
}
if ((params.align - 1) & params.align) {
+ LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
@@ -92,10 +95,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
+ LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
if (object->status == Object::Status::Allocated) {
+ LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::OperationNotPermitted);
}
@@ -116,11 +121,13 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "called");
if (!params.handle) {
+ LOG_ERROR(Service_NVDRV, "Handle is zero");
return static_cast<u32>(NvErrCodes::InvalidValue);
}
auto object = GetObject(params.handle);
if (!object) {
+ LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::OperationNotPermitted);
}
@@ -139,11 +146,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = std::find_if(handles.begin(), handles.end(),
[&](const auto& entry) { return entry.second->id == params.id; });
if (itr == handles.end()) {
+ LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
auto& object = itr->second;
if (object->status != Object::Status::Allocated) {
+ LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
@@ -166,10 +175,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
+ LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
if (object->status != Object::Status::Allocated) {
+ LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::OperationNotPermitted);
}
@@ -209,9 +220,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = handles.find(params.handle);
if (itr == handles.end()) {
+ LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
if (!itr->second->refcount) {
+ LOG_ERROR(
+ Service_NVDRV,
+ "There is no references to this object. The object is already freed. handle={:08X}",
+ params.handle);
return static_cast<u32>(NvErrCodes::InvalidValue);
}
diff --git a/src/core/hle/service/nvdrv/interface.cpp b/src/core/hle/service/nvdrv/interface.cpp
index ac3859353..3b9ab4b14 100644
--- a/src/core/hle/service/nvdrv/interface.cpp
+++ b/src/core/hle/service/nvdrv/interface.cpp
@@ -6,7 +6,9 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdrv.h"
@@ -55,6 +57,7 @@ void NVDRV::Close(Kernel::HLERequestContext& ctx) {
void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
@@ -68,15 +71,15 @@ void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(query_event);
+ rb.PushCopyObjects(query_event.readable);
rb.Push<u32>(0);
}
void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
pid = rp.Pop<u64>();
-
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid=0x{:X}", pid);
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
@@ -84,6 +87,23 @@ void NVDRV::SetClientPID(Kernel::HLERequestContext& ctx) {
void NVDRV::FinishInitialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
+ // According to SwitchBrew, this has no inputs and no outputs, so effectively does nothing on
+ // retail hardware.
+ LOG_DEBUG(Service_NVDRV, "called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -97,10 +117,10 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
{3, &NVDRV::Initialize, "Initialize"},
{4, &NVDRV::QueryEvent, "QueryEvent"},
{5, nullptr, "MapSharedMem"},
- {6, nullptr, "GetStatus"},
+ {6, &NVDRV::GetStatus, "GetStatus"},
{7, nullptr, "ForceSetClientPID"},
{8, &NVDRV::SetClientPID, "SetClientPID"},
- {9, nullptr, "DumpGraphicsMemoryInfo"},
+ {9, &NVDRV::DumpGraphicsMemoryInfo, "DumpGraphicsMemoryInfo"},
{10, nullptr, "InitializeDevtools"},
{11, &NVDRV::Ioctl, "Ioctl2"},
{12, nullptr, "Ioctl3"},
@@ -109,7 +129,8 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
RegisterHandlers(functions);
auto& kernel = Core::System::GetInstance().Kernel();
- query_event = Kernel::Event::Create(kernel, Kernel::ResetType::OneShot, "NVDRV::query_event");
+ query_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::OneShot,
+ "NVDRV::query_event");
}
NVDRV::~NVDRV() = default;
diff --git a/src/core/hle/service/nvdrv/interface.h b/src/core/hle/service/nvdrv/interface.h
index d340893c2..fe311b069 100644
--- a/src/core/hle/service/nvdrv/interface.h
+++ b/src/core/hle/service/nvdrv/interface.h
@@ -5,10 +5,13 @@
#pragma once
#include <memory>
-#include "core/hle/kernel/event.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/service.h"
+namespace Kernel {
+class WritableEvent;
+}
+
namespace Service::Nvidia {
class NVDRV final : public ServiceFramework<NVDRV> {
@@ -24,12 +27,14 @@ private:
void QueryEvent(Kernel::HLERequestContext& ctx);
void SetClientPID(Kernel::HLERequestContext& ctx);
void FinishInitialize(Kernel::HLERequestContext& ctx);
+ void GetStatus(Kernel::HLERequestContext& ctx);
+ void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
std::shared_ptr<Module> nvdrv;
u64 pid{};
- Kernel::SharedPtr<Kernel::Event> query_event;
+ Kernel::EventPair query_event;
};
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index fd98d541d..fc07d9bb8 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -7,31 +7,34 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
namespace Service::NVFlinger {
BufferQueue::BufferQueue(u32 id, u64 layer_id) : id(id), layer_id(layer_id) {
auto& kernel = Core::System::GetInstance().Kernel();
- buffer_wait_event =
- Kernel::Event::Create(kernel, Kernel::ResetType::Sticky, "BufferQueue NativeHandle");
+ buffer_wait_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
+ "BufferQueue NativeHandle");
}
BufferQueue::~BufferQueue() = default;
void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer) {
+ LOG_WARNING(Service, "Adding graphics buffer {}", slot);
+
Buffer buffer{};
buffer.slot = slot;
buffer.igbp_buffer = igbp_buffer;
buffer.status = Buffer::Status::Free;
- LOG_WARNING(Service, "Adding graphics buffer {}", slot);
-
queue.emplace_back(buffer);
- buffer_wait_event->Signal();
+ buffer_wait_event.writable->Signal();
}
-boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
+std::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
auto itr = std::find_if(queue.begin(), queue.end(), [&](const Buffer& buffer) {
// Only consider free buffers. Buffers become free once again after they've been Acquired
// and Released by the compositor, see the NVFlinger::Compose method.
@@ -44,7 +47,7 @@ boost::optional<u32> BufferQueue::DequeueBuffer(u32 width, u32 height) {
});
if (itr == queue.end()) {
- return boost::none;
+ return {};
}
itr->status = Buffer::Status::Dequeued;
@@ -70,12 +73,12 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
itr->crop_rect = crop_rect;
}
-boost::optional<const BufferQueue::Buffer&> BufferQueue::AcquireBuffer() {
+std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
auto itr = std::find_if(queue.begin(), queue.end(), [](const Buffer& buffer) {
return buffer.status == Buffer::Status::Queued;
});
if (itr == queue.end())
- return boost::none;
+ return {};
itr->status = Buffer::Status::Acquired;
return *itr;
}
@@ -87,11 +90,12 @@ void BufferQueue::ReleaseBuffer(u32 slot) {
ASSERT(itr->status == Buffer::Status::Acquired);
itr->status = Buffer::Status::Free;
- buffer_wait_event->Signal();
+ buffer_wait_event.writable->Signal();
}
u32 BufferQueue::Query(QueryType type) {
LOG_WARNING(Service, "(STUBBED) called type={}", static_cast<u32>(type));
+
switch (type) {
case QueryType::NativeWindowFormat:
// TODO(Subv): Use an enum for this
@@ -103,4 +107,12 @@ u32 BufferQueue::Query(QueryType type) {
return 0;
}
+Kernel::SharedPtr<Kernel::WritableEvent> BufferQueue::GetWritableBufferWaitEvent() const {
+ return buffer_wait_event.writable;
+}
+
+Kernel::SharedPtr<Kernel::ReadableEvent> BufferQueue::GetBufferWaitEvent() const {
+ return buffer_wait_event.readable;
+}
+
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 50b767732..b171f256c 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -4,12 +4,14 @@
#pragma once
+#include <optional>
#include <vector>
-#include <boost/optional.hpp>
+
#include "common/common_funcs.h"
#include "common/math_util.h"
#include "common/swap.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/object.h"
+#include "core/hle/kernel/writable_event.h"
namespace CoreTiming {
struct EventType;
@@ -57,9 +59,9 @@ public:
/// Rotate source image 90 degrees clockwise
Rotate90 = 0x04,
/// Rotate source image 180 degrees
- Roate180 = 0x03,
+ Rotate180 = 0x03,
/// Rotate source image 270 degrees clockwise
- Roate270 = 0x07,
+ Rotate270 = 0x07,
};
struct Buffer {
@@ -73,11 +75,11 @@ public:
};
void SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer);
- boost::optional<u32> DequeueBuffer(u32 width, u32 height);
+ std::optional<u32> DequeueBuffer(u32 width, u32 height);
const IGBPBuffer& RequestBuffer(u32 slot) const;
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const MathUtil::Rectangle<int>& crop_rect);
- boost::optional<const Buffer&> AcquireBuffer();
+ std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
u32 Query(QueryType type);
@@ -85,16 +87,16 @@ public:
return id;
}
- Kernel::SharedPtr<Kernel::Event> GetBufferWaitEvent() const {
- return buffer_wait_event;
- }
+ Kernel::SharedPtr<Kernel::WritableEvent> GetWritableBufferWaitEvent() const;
+
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetBufferWaitEvent() const;
private:
u32 id;
u64 layer_id;
std::vector<Buffer> queue;
- Kernel::SharedPtr<Kernel::Event> buffer_wait_event;
+ Kernel::EventPair buffer_wait_event;
};
} // namespace Service::NVFlinger
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index d47b6f659..05af2d593 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -3,7 +3,7 @@
// Refer to the license.txt file included.
#include <algorithm>
-#include <boost/optional.hpp>
+#include <optional>
#include "common/alignment.h"
#include "common/assert.h"
@@ -13,6 +13,9 @@
#include "core/core.h"
#include "core/core_timing.h"
#include "core/core_timing_util.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/devices/nvdisp_disp0.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
@@ -83,9 +86,8 @@ u32 NVFlinger::GetBufferQueueId(u64 display_id, u64 layer_id) {
return layer.buffer_queue->GetId();
}
-Kernel::SharedPtr<Kernel::Event> NVFlinger::GetVsyncEvent(u64 display_id) {
- const auto& display = GetDisplay(display_id);
- return display.vsync_event;
+Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id) {
+ return GetDisplay(display_id).vsync_event.readable;
}
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
@@ -117,7 +119,7 @@ Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
void NVFlinger::Compose() {
for (auto& display : displays) {
// Trigger vsync for this display at the end of drawing
- SCOPE_EXIT({ display.vsync_event->Signal(); });
+ SCOPE_EXIT({ display.vsync_event.writable->Signal(); });
// Don't do anything for displays without layers.
if (display.layers.empty())
@@ -134,7 +136,7 @@ void NVFlinger::Compose() {
MicroProfileFlip();
- if (buffer == boost::none) {
+ if (!buffer) {
auto& system_instance = Core::System::GetInstance();
// There was no queued buffer to draw, render previous frame
@@ -143,7 +145,7 @@ void NVFlinger::Compose() {
continue;
}
- auto& igbp_buffer = buffer->igbp_buffer;
+ auto& igbp_buffer = buffer->get().igbp_buffer;
// Now send the buffer to the GPU for drawing.
// TODO(Subv): Support more than just disp0. The display device selection is probably based
@@ -152,10 +154,10 @@ void NVFlinger::Compose() {
ASSERT(nvdisp);
nvdisp->flip(igbp_buffer.gpu_buffer_id, igbp_buffer.offset, igbp_buffer.format,
- igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride, buffer->transform,
- buffer->crop_rect);
+ igbp_buffer.width, igbp_buffer.height, igbp_buffer.stride,
+ buffer->get().transform, buffer->get().crop_rect);
- buffer_queue->ReleaseBuffer(buffer->slot);
+ buffer_queue->ReleaseBuffer(buffer->get().slot);
}
}
@@ -164,7 +166,8 @@ Layer::~Layer() = default;
Display::Display(u64 id, std::string name) : id(id), name(std::move(name)) {
auto& kernel = Core::System::GetInstance().Kernel();
- vsync_event = Kernel::Event::Create(kernel, Kernel::ResetType::Pulse, "Display VSync Event");
+ vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse,
+ fmt::format("Display VSync Event {}", id));
}
Display::~Display() = default;
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 3dc69e69b..9abba555b 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -10,12 +10,17 @@
#include <vector>
#include "common/common_types.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/object.h"
namespace CoreTiming {
struct EventType;
}
+namespace Kernel {
+class ReadableEvent;
+class WritableEvent;
+} // namespace Kernel
+
namespace Service::Nvidia {
class Module;
}
@@ -40,7 +45,7 @@ struct Display {
std::string name;
std::vector<Layer> layers;
- Kernel::SharedPtr<Kernel::Event> vsync_event;
+ Kernel::EventPair vsync_event;
};
class NVFlinger final {
@@ -61,7 +66,7 @@ public:
u32 GetBufferQueueId(u64 display_id, u64 layer_id);
/// Gets the vsync event for the specified display.
- Kernel::SharedPtr<Kernel::Event> GetVsyncEvent(u64 display_id);
+ Kernel::SharedPtr<Kernel::ReadableEvent> GetVsyncEvent(u64 display_id);
/// Obtains a buffer queue identified by the id.
std::shared_ptr<BufferQueue> GetBufferQueue(u32 id) const;
diff --git a/src/core/hle/service/pctl/module.cpp b/src/core/hle/service/pctl/module.cpp
index 4fd185f69..6081f41e1 100644
--- a/src/core/hle/service/pctl/module.cpp
+++ b/src/core/hle/service/pctl/module.cpp
@@ -114,29 +114,33 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 0};
rb.Push(RESULT_SUCCESS);
}
void CheckFreeCommunicationPermission(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_PCTL, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
};
void Module::Interface::CreateService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PCTL, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IParentalControlService>();
- LOG_DEBUG(Service_PCTL, "called");
}
void Module::Interface::CreateServiceWithoutInitialize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PCTL, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IParentalControlService>();
- LOG_DEBUG(Service_PCTL, "called");
}
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 6ec35ca60..53e7da9c3 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -20,11 +20,11 @@ public:
private:
void GetBootMode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PM, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(static_cast<u32>(SystemBootMode::Normal)); // Normal boot mode
-
- LOG_DEBUG(Service_PM, "called");
}
};
diff --git a/src/core/hle/service/psc/psc.cpp b/src/core/hle/service/psc/psc.cpp
index bbad870a2..0ba0a4076 100644
--- a/src/core/hle/service/psc/psc.cpp
+++ b/src/core/hle/service/psc/psc.cpp
@@ -61,11 +61,11 @@ public:
private:
void GetPmModule(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PSC, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IPmModule>();
-
- LOG_DEBUG(Service_PSC, "called");
}
};
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index a4cf45267..d25b80ab0 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -70,18 +70,14 @@
#include "core/hle/service/vi/vi.h"
#include "core/hle/service/wlan/wlan.h"
-using Kernel::ClientPort;
-using Kernel::ServerPort;
-using Kernel::SharedPtr;
-
namespace Service {
/**
* Creates a function string for logging, complete with the name (or header code, depending
* on what's passed in) the port name, and all the cmd_buff arguments.
*/
-static std::string MakeFunctionString(const char* name, const char* port_name,
- const u32* cmd_buff) {
+[[maybe_unused]] static std::string MakeFunctionString(const char* name, const char* port_name,
+ const u32* cmd_buff) {
// Number of params == bits 0-5 + bits 6-11
int num_params = (cmd_buff[0] & 0x3F) + ((cmd_buff[0] >> 6) & 0x3F);
@@ -101,33 +97,33 @@ ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_ses
ServiceFrameworkBase::~ServiceFrameworkBase() = default;
void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager) {
- ASSERT(port == nullptr);
- port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
+ ASSERT(!port_installed);
+
+ auto port = service_manager.RegisterService(service_name, max_sessions).Unwrap();
port->SetHleHandler(shared_from_this());
+ port_installed = true;
}
void ServiceFrameworkBase::InstallAsNamedPort() {
- ASSERT(port == nullptr);
+ ASSERT(!port_installed);
auto& kernel = Core::System::GetInstance().Kernel();
- SharedPtr<ServerPort> server_port;
- SharedPtr<ClientPort> client_port;
- std::tie(server_port, client_port) =
- ServerPort::CreatePortPair(kernel, max_sessions, service_name);
+ auto [server_port, client_port] =
+ Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
server_port->SetHleHandler(shared_from_this());
kernel.AddNamedPort(service_name, std::move(client_port));
+ port_installed = true;
}
Kernel::SharedPtr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
- ASSERT(port == nullptr);
+ ASSERT(!port_installed);
auto& kernel = Core::System::GetInstance().Kernel();
- Kernel::SharedPtr<Kernel::ServerPort> server_port;
- Kernel::SharedPtr<Kernel::ClientPort> client_port;
- std::tie(server_port, client_port) =
+ auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
- port = MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port)).Unwrap();
+ auto port = MakeResult(std::move(server_port)).Unwrap();
port->SetHleHandler(shared_from_this());
+ port_installed = true;
return client_port;
}
@@ -152,8 +148,7 @@ void ServiceFrameworkBase::ReportUnimplementedFunction(Kernel::HLERequestContext
}
buf.push_back('}');
- LOG_ERROR(Service, "unknown / unimplemented {}", fmt::to_string(buf));
- UNIMPLEMENTED();
+ UNIMPLEMENTED_MSG("Unknown / unimplemented {}", fmt::to_string(buf));
}
void ServiceFrameworkBase::InvokeRequest(Kernel::HLERequestContext& ctx) {
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 98483ecf1..029533628 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -96,11 +96,9 @@ private:
/// Maximum number of concurrent sessions that this service can handle.
u32 max_sessions;
- /**
- * Port where incoming connections will be received. Only created when InstallAsService() or
- * InstallAsNamedPort() are called.
- */
- Kernel::SharedPtr<Kernel::ServerPort> port;
+ /// Flag to store if a port was already create/installed to detect multiple install attempts,
+ /// which is not supported.
+ bool port_installed = false;
/// Function used to safely up-cast pointers to the derived class before invoking a handler.
InvokerFn* handler_invoker;
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index 9e5af7839..1afc43f75 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -35,6 +35,8 @@ constexpr std::array<LanguageCode, 17> available_language_codes = {{
constexpr std::size_t pre4_0_0_max_entries = 0xF;
constexpr std::size_t post4_0_0_max_entries = 0x40;
+constexpr ResultCode ERR_INVALID_LANGUAGE{ErrorModule::Settings, 625};
+
LanguageCode GetLanguageCodeFromIndex(std::size_t index) {
return available_language_codes.at(index);
}
@@ -49,38 +51,54 @@ static std::array<LanguageCode, size> MakeLanguageCodeSubset() {
static void PushResponseLanguageCode(Kernel::HLERequestContext& ctx, std::size_t max_size) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- if (available_language_codes.size() > max_size)
+ if (available_language_codes.size() > max_size) {
rb.Push(static_cast<u32>(max_size));
- else
+ } else {
rb.Push(static_cast<u32>(available_language_codes.size()));
+ }
}
void SET::GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx) {
- if (available_language_codes.size() > pre4_0_0_max_entries)
+ LOG_DEBUG(Service_SET, "called");
+
+ if (available_language_codes.size() > pre4_0_0_max_entries) {
ctx.WriteBuffer(MakeLanguageCodeSubset<pre4_0_0_max_entries>());
- else
+ } else {
ctx.WriteBuffer(available_language_codes);
-
+ }
PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
+}
- LOG_DEBUG(Service_SET, "called");
+void SET::MakeLanguageCode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto index = rp.Pop<u32>();
+
+ if (index >= available_language_codes.size()) {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ERR_INVALID_LANGUAGE);
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(available_language_codes[index]);
}
void SET::GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx) {
- if (available_language_codes.size() > post4_0_0_max_entries)
+ LOG_DEBUG(Service_SET, "called");
+
+ if (available_language_codes.size() > post4_0_0_max_entries) {
ctx.WriteBuffer(MakeLanguageCodeSubset<post4_0_0_max_entries>());
- else
+ } else {
ctx.WriteBuffer(available_language_codes);
-
+ }
PushResponseLanguageCode(ctx, post4_0_0_max_entries);
-
- LOG_DEBUG(Service_SET, "called");
}
void SET::GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx) {
- PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
-
LOG_DEBUG(Service_SET, "called");
+
+ PushResponseLanguageCode(ctx, pre4_0_0_max_entries);
}
void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
@@ -90,18 +108,18 @@ void SET::GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx) {
}
void SET::GetLanguageCode(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(static_cast<u64>(available_language_codes[Settings::values.language_index]));
-
- LOG_DEBUG(Service_SET, "called {}", Settings::values.language_index);
+ rb.PushEnum(available_language_codes[Settings::values.language_index]);
}
SET::SET() : ServiceFramework("set") {
static const FunctionInfo functions[] = {
{0, &SET::GetLanguageCode, "GetLanguageCode"},
{1, &SET::GetAvailableLanguageCodes, "GetAvailableLanguageCodes"},
- {2, nullptr, "MakeLanguageCode"},
+ {2, &SET::MakeLanguageCode, "MakeLanguageCode"},
{3, &SET::GetAvailableLanguageCodeCount, "GetAvailableLanguageCodeCount"},
{4, nullptr, "GetRegionCode"},
{5, &SET::GetAvailableLanguageCodes2, "GetAvailableLanguageCodes2"},
diff --git a/src/core/hle/service/set/set.h b/src/core/hle/service/set/set.h
index 266f13e46..31f9cb296 100644
--- a/src/core/hle/service/set/set.h
+++ b/src/core/hle/service/set/set.h
@@ -38,6 +38,7 @@ public:
private:
void GetLanguageCode(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodes(Kernel::HLERequestContext& ctx);
+ void MakeLanguageCode(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodes2(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodeCount(Kernel::HLERequestContext& ctx);
void GetAvailableLanguageCodeCount2(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 41efca31c..c9b4da5b0 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -10,22 +10,22 @@
namespace Service::Set {
void SET_SYS::GetColorSetId(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.PushEnum(color_set);
-
- LOG_DEBUG(Service_SET, "called");
}
void SET_SYS::SetColorSetId(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SET, "called");
+
IPC::RequestParser rp{ctx};
color_set = rp.PopEnum<ColorSet>();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- LOG_DEBUG(Service_SET, "called");
}
SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
diff --git a/src/core/hle/service/sm/controller.cpp b/src/core/hle/service/sm/controller.cpp
index 98f6e4111..74da4d5e6 100644
--- a/src/core/hle/service/sm/controller.cpp
+++ b/src/core/hle/service/sm/controller.cpp
@@ -14,25 +14,26 @@ namespace Service::SM {
void Controller::ConvertSessionToDomain(Kernel::HLERequestContext& ctx) {
ASSERT_MSG(ctx.Session()->IsSession(), "Session is already a domain");
+ LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
ctx.Session()->ConvertToDomain();
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(1); // Converted sessions start with 1 request handler
-
- LOG_DEBUG(Service, "called, server_session={}", ctx.Session()->GetObjectId());
}
void Controller::DuplicateSession(Kernel::HLERequestContext& ctx) {
// TODO(bunnei): This is just creating a new handle to the same Session. I assume this is wrong
// and that we probably want to actually make an entirely new Session, but we still need to
// verify this on hardware.
+ LOG_DEBUG(Service, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
rb.Push(RESULT_SUCCESS);
Kernel::SharedPtr<Kernel::ClientSession> session{ctx.Session()->parent->client};
rb.PushMoveObjects(session);
- LOG_DEBUG(Service, "called, session={}", session->GetObjectId());
+ LOG_DEBUG(Service, "session={}", session->GetObjectId());
}
void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
@@ -42,11 +43,11 @@ void Controller::DuplicateSessionEx(Kernel::HLERequestContext& ctx) {
}
void Controller::QueryPointerBufferSize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u16>(0x500);
-
- LOG_WARNING(Service, "(STUBBED) called");
}
Controller::Controller() : ServiceFramework("IpcController") {
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 464e79d01..142929124 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -54,13 +54,22 @@ ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> ServiceManager::RegisterService
return ERR_ALREADY_REGISTERED;
auto& kernel = Core::System::GetInstance().Kernel();
- Kernel::SharedPtr<Kernel::ServerPort> server_port;
- Kernel::SharedPtr<Kernel::ClientPort> client_port;
- std::tie(server_port, client_port) =
+ auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
registered_services.emplace(std::move(name), std::move(client_port));
- return MakeResult<Kernel::SharedPtr<Kernel::ServerPort>>(std::move(server_port));
+ return MakeResult(std::move(server_port));
+}
+
+ResultCode ServiceManager::UnregisterService(const std::string& name) {
+ CASCADE_CODE(ValidateServiceName(name));
+
+ const auto iter = registered_services.find(name);
+ if (iter == registered_services.end())
+ return ERR_SERVICE_NOT_REGISTERED;
+
+ registered_services.erase(iter);
+ return RESULT_SUCCESS;
}
ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort(
@@ -72,7 +81,7 @@ ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> ServiceManager::GetServicePort(
return ERR_SERVICE_NOT_REGISTERED;
}
- return MakeResult<Kernel::SharedPtr<Kernel::ClientPort>>(it->second);
+ return MakeResult(it->second);
}
ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ServiceManager::ConnectToService(
@@ -92,9 +101,10 @@ SM::~SM() = default;
* 0: ResultCode
*/
void SM::Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SM, "called");
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_SM, "called");
}
void SM::GetService(Kernel::HLERequestContext& ctx) {
@@ -127,13 +137,54 @@ void SM::GetService(Kernel::HLERequestContext& ctx) {
}
}
+void SM::RegisterService(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto name_buf = rp.PopRaw<std::array<char, 8>>();
+ const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
+
+ const std::string name(name_buf.begin(), end);
+
+ const auto is_light = static_cast<bool>(rp.PopRaw<u32>());
+ const auto max_session_count = rp.PopRaw<u32>();
+
+ LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
+ max_session_count, is_light);
+
+ auto handle = service_manager->RegisterService(name, max_session_count);
+ if (handle.Failed()) {
+ LOG_ERROR(Service_SM, "failed to register service with error_code={:08X}",
+ handle.Code().raw);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(handle.Code());
+ return;
+ }
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1, IPC::ResponseBuilder::Flags::AlwaysMoveHandles};
+ rb.Push(handle.Code());
+ rb.PushMoveObjects(std::move(handle).Unwrap());
+}
+
+void SM::UnregisterService(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const auto name_buf = rp.PopRaw<std::array<char, 8>>();
+ const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
+
+ const std::string name(name_buf.begin(), end);
+ LOG_DEBUG(Service_SM, "called with name={}", name);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(service_manager->UnregisterService(name));
+}
+
SM::SM(std::shared_ptr<ServiceManager> service_manager)
: ServiceFramework("sm:", 4), service_manager(std::move(service_manager)) {
static const FunctionInfo functions[] = {
{0x00000000, &SM::Initialize, "Initialize"},
{0x00000001, &SM::GetService, "GetService"},
- {0x00000002, nullptr, "RegisterService"},
- {0x00000003, nullptr, "UnregisterService"},
+ {0x00000002, &SM::RegisterService, "RegisterService"},
+ {0x00000003, &SM::UnregisterService, "UnregisterService"},
};
RegisterHandlers(functions);
}
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index 4f8145dda..bef25433e 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -35,6 +35,8 @@ public:
private:
void Initialize(Kernel::HLERequestContext& ctx);
void GetService(Kernel::HLERequestContext& ctx);
+ void RegisterService(Kernel::HLERequestContext& ctx);
+ void UnregisterService(Kernel::HLERequestContext& ctx);
std::shared_ptr<ServiceManager> service_manager;
};
@@ -48,6 +50,7 @@ public:
ResultVal<Kernel::SharedPtr<Kernel::ServerPort>> RegisterService(std::string name,
unsigned int max_sessions);
+ ResultCode UnregisterService(const std::string& name);
ResultVal<Kernel::SharedPtr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<Kernel::SharedPtr<Kernel::ClientSession>> ConnectToService(const std::string& name);
diff --git a/src/core/hle/service/spl/module.cpp b/src/core/hle/service/spl/module.cpp
index 44a6717d0..8db0c2f13 100644
--- a/src/core/hle/service/spl/module.cpp
+++ b/src/core/hle/service/spl/module.cpp
@@ -3,34 +3,41 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <chrono>
#include <cstdlib>
+#include <ctime>
+#include <functional>
#include <vector>
#include "common/logging/log.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/spl/csrng.h"
#include "core/hle/service/spl/module.h"
#include "core/hle/service/spl/spl.h"
+#include "core/settings.h"
namespace Service::SPL {
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
- : ServiceFramework(name), module(std::move(module)) {}
+ : ServiceFramework(name), module(std::move(module)),
+ rng(Settings::values.rng_seed.value_or(std::time(nullptr))) {}
Module::Interface::~Interface() = default;
void Module::Interface::GetRandomBytes(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_SPL, "called");
+
IPC::RequestParser rp{ctx};
std::size_t size = ctx.GetWriteBufferSize();
+ std::uniform_int_distribution<u16> distribution(0, std::numeric_limits<u8>::max());
std::vector<u8> data(size);
- std::generate(data.begin(), data.end(), std::rand);
+ std::generate(data.begin(), data.end(), [&] { return static_cast<u8>(distribution(rng)); });
ctx.WriteBuffer(data);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- LOG_DEBUG(Service_SPL, "called");
}
void InstallInterfaces(SM::ServiceManager& service_manager) {
diff --git a/src/core/hle/service/spl/module.h b/src/core/hle/service/spl/module.h
index 48fda6099..afa1f0295 100644
--- a/src/core/hle/service/spl/module.h
+++ b/src/core/hle/service/spl/module.h
@@ -4,6 +4,7 @@
#pragma once
+#include <random>
#include "core/hle/service/service.h"
namespace Service::SPL {
@@ -19,6 +20,9 @@ public:
protected:
std::shared_ptr<Module> module;
+
+ private:
+ std::mt19937 rng;
};
};
diff --git a/src/core/hle/service/ssl/ssl.cpp b/src/core/hle/service/ssl/ssl.cpp
index bc4f7a437..af40a1815 100644
--- a/src/core/hle/service/ssl/ssl.cpp
+++ b/src/core/hle/service/ssl/ssl.cpp
@@ -69,6 +69,7 @@ public:
private:
void SetOption(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_SSL, "(STUBBED) called");
+
IPC::RequestParser rp{ctx};
IPC::ResponseBuilder rb{ctx, 2};
@@ -114,6 +115,7 @@ private:
void SetInterfaceVersion(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_SSL, "called");
+
IPC::RequestParser rp{ctx};
ssl_version = rp.Pop<u32>();
diff --git a/src/core/hle/service/time/interface.cpp b/src/core/hle/service/time/interface.cpp
index 18a5d71d5..b3a196f65 100644
--- a/src/core/hle/service/time/interface.cpp
+++ b/src/core/hle/service/time/interface.cpp
@@ -21,9 +21,10 @@ Time::Time(std::shared_ptr<Module> time, const char* name)
{102, nullptr, "GetStandardUserSystemClockInitialYear"},
{200, nullptr, "IsStandardNetworkSystemClockAccuracySufficient"},
{300, nullptr, "CalculateMonotonicSystemClockBaseTimePoint"},
- {400, nullptr, "GetClockSnapshot"},
+ {400, &Time::GetClockSnapshot, "GetClockSnapshot"},
{401, nullptr, "GetClockSnapshotFromSystemClockContext"},
- {500, nullptr, "CalculateStandardUserSystemClockDifferenceByUser"},
+ {500, &Time::CalculateStandardUserSystemClockDifferenceByUser,
+ "CalculateStandardUserSystemClockDifferenceByUser"},
{501, nullptr, "CalculateSpanBetween"},
};
RegisterHandlers(functions);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 28fd8debc..16564de24 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -15,6 +15,44 @@
namespace Service::Time {
+static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
+ CalendarAdditionalInfo& additional_info,
+ [[maybe_unused]] const TimeZoneRule& /*rule*/) {
+ const std::time_t time(posix_time);
+ const std::tm* tm = std::localtime(&time);
+ if (tm == nullptr) {
+ calendar_time = {};
+ additional_info = {};
+ return;
+ }
+ calendar_time.year = tm->tm_year + 1900;
+ calendar_time.month = tm->tm_mon + 1;
+ calendar_time.day = tm->tm_mday;
+ calendar_time.hour = tm->tm_hour;
+ calendar_time.minute = tm->tm_min;
+ calendar_time.second = tm->tm_sec;
+
+ additional_info.day_of_week = tm->tm_wday;
+ additional_info.day_of_year = tm->tm_yday;
+ std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
+ additional_info.utc_offset = 0;
+}
+
+static u64 CalendarToPosix(const CalendarTime& calendar_time,
+ [[maybe_unused]] const TimeZoneRule& /*rule*/) {
+ std::tm time{};
+ time.tm_year = calendar_time.year - 1900;
+ time.tm_mon = calendar_time.month - 1;
+ time.tm_mday = calendar_time.day;
+
+ time.tm_hour = calendar_time.hour;
+ time.tm_min = calendar_time.minute;
+ time.tm_sec = calendar_time.second;
+
+ std::time_t epoch_time = std::mktime(&time);
+ return static_cast<u64>(epoch_time);
+}
+
class ISystemClock final : public ServiceFramework<ISystemClock> {
public:
ISystemClock() : ServiceFramework("ISystemClock") {
@@ -34,6 +72,7 @@ private:
std::chrono::system_clock::now().time_since_epoch())
.count()};
LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(time_since_epoch);
@@ -41,6 +80,7 @@ private:
void GetSystemClockContext(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Time, "(STUBBED) called");
+
SystemClockContext system_clock_ontext{};
IPC::ResponseBuilder rb{ctx, (sizeof(SystemClockContext) / 4) + 2};
rb.Push(RESULT_SUCCESS);
@@ -60,6 +100,7 @@ public:
private:
void GetCurrentTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
+
SteadyClockTimePoint steady_clock_time_point{
CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000};
IPC::ResponseBuilder rb{ctx, (sizeof(SteadyClockTimePoint) / 4) + 2};
@@ -80,8 +121,8 @@ public:
{5, nullptr, "GetTimeZoneRuleVersion"},
{100, &ITimeZoneService::ToCalendarTime, "ToCalendarTime"},
{101, &ITimeZoneService::ToCalendarTimeWithMyRule, "ToCalendarTimeWithMyRule"},
- {201, nullptr, "ToPosixTime"},
- {202, nullptr, "ToPosixTimeWithMyRule"},
+ {201, &ITimeZoneService::ToPosixTime, "ToPosixTime"},
+ {202, &ITimeZoneService::ToPosixTimeWithMyRule, "ToPosixTimeWithMyRule"},
};
RegisterHandlers(functions);
}
@@ -92,6 +133,7 @@ private:
void GetDeviceLocationName(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, (sizeof(LocationName) / 4) + 2};
rb.Push(RESULT_SUCCESS);
rb.PushRaw(location_name);
@@ -99,6 +141,7 @@ private:
void GetTotalLocationNameCount(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_Time, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(0);
@@ -116,7 +159,6 @@ private:
void ToCalendarTime(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 posix_time = rp.Pop<u64>();
-
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
TimeZoneRule time_zone_rule{};
@@ -137,7 +179,6 @@ private:
void ToCalendarTimeWithMyRule(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 posix_time = rp.Pop<u64>();
-
LOG_WARNING(Service_Time, "(STUBBED) called, posix_time=0x{:016X}", posix_time);
CalendarTime calendar_time{2018, 1, 1, 0, 0, 0};
@@ -151,60 +192,139 @@ private:
rb.PushRaw(additional_info);
}
- void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
- CalendarAdditionalInfo& additional_info, const TimeZoneRule& /*rule*/) {
- std::time_t t(posix_time);
- std::tm* tm = std::localtime(&t);
- if (!tm) {
- return;
- }
- calendar_time.year = tm->tm_year + 1900;
- calendar_time.month = tm->tm_mon + 1;
- calendar_time.day = tm->tm_mday;
- calendar_time.hour = tm->tm_hour;
- calendar_time.minute = tm->tm_min;
- calendar_time.second = tm->tm_sec;
-
- additional_info.day_of_week = tm->tm_wday;
- additional_info.day_of_year = tm->tm_yday;
- std::memcpy(additional_info.name.data(), "UTC", sizeof("UTC"));
- additional_info.utc_offset = 0;
+ void ToPosixTime(Kernel::HLERequestContext& ctx) {
+ // TODO(ogniK): Figure out how to handle multiple times
+ LOG_WARNING(Service_Time, "(STUBBED) called");
+
+ IPC::RequestParser rp{ctx};
+ auto calendar_time = rp.PopRaw<CalendarTime>();
+ auto posix_time = CalendarToPosix(calendar_time, {});
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(1); // Amount of times we're returning
+ ctx.WriteBuffer(&posix_time, sizeof(u64));
+ }
+
+ void ToPosixTimeWithMyRule(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_Time, "(STUBBED) called");
+
+ IPC::RequestParser rp{ctx};
+ auto calendar_time = rp.PopRaw<CalendarTime>();
+ auto posix_time = CalendarToPosix(calendar_time, {});
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u32>(1); // Amount of times we're returning
+ ctx.WriteBuffer(&posix_time, sizeof(u64));
}
};
void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>();
- LOG_DEBUG(Service_Time, "called");
}
void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>();
- LOG_DEBUG(Service_Time, "called");
}
void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISteadyClock>();
- LOG_DEBUG(Service_Time, "called");
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ITimeZoneService>();
- LOG_DEBUG(Service_Time, "called");
}
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<ISystemClock>();
+}
+
+void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto initial_type = rp.PopRaw<u8>();
+
+ const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch())
+ .count()};
+
+ const std::time_t time(time_since_epoch);
+ const std::tm* tm = std::localtime(&time);
+ if (tm == nullptr) {
+ LOG_ERROR(Service_Time, "tm is a nullptr");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
+ return;
+ }
+
+ const SteadyClockTimePoint steady_clock_time_point{
+ CoreTiming::cyclesToMs(CoreTiming::GetTicks()) / 1000, {}};
+
+ CalendarTime calendar_time{};
+ calendar_time.year = tm->tm_year + 1900;
+ calendar_time.month = tm->tm_mon + 1;
+ calendar_time.day = tm->tm_mday;
+ calendar_time.hour = tm->tm_hour;
+ calendar_time.minute = tm->tm_min;
+ calendar_time.second = tm->tm_sec;
+
+ ClockSnapshot clock_snapshot{};
+ clock_snapshot.system_posix_time = time_since_epoch;
+ clock_snapshot.network_posix_time = time_since_epoch;
+ clock_snapshot.system_calendar_time = calendar_time;
+ clock_snapshot.network_calendar_time = calendar_time;
+
+ CalendarAdditionalInfo additional_info{};
+ PosixToCalendar(time_since_epoch, calendar_time, additional_info, {});
+
+ clock_snapshot.system_calendar_info = additional_info;
+ clock_snapshot.network_calendar_info = additional_info;
+
+ clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
+ clock_snapshot.location_name = LocationName{"UTC"};
+ clock_snapshot.clock_auto_adjustment_enabled = 1;
+ clock_snapshot.type = initial_type;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
+}
+
+void Module::Interface::CalculateStandardUserSystemClockDifferenceByUser(
+ Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_Time, "called");
+
+ IPC::RequestParser rp{ctx};
+ const auto snapshot_a = rp.PopRaw<ClockSnapshot>();
+ const auto snapshot_b = rp.PopRaw<ClockSnapshot>();
+ const u64 difference =
+ snapshot_b.user_clock_context.offset - snapshot_a.user_clock_context.offset;
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u64>(difference);
}
Module::Interface::Interface(std::shared_ptr<Module> time, const char* name)
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 5659ecad3..f11affe95 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include "common/common_funcs.h"
#include "core/hle/service/service.h"
namespace Service::Time {
@@ -21,7 +22,6 @@ struct CalendarTime {
u8 hour;
u8 minute;
u8 second;
- INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(CalendarTime) == 0x8, "CalendarTime structure has incorrect size");
@@ -29,7 +29,7 @@ struct CalendarAdditionalInfo {
u32_le day_of_week;
u32_le day_of_year;
std::array<u8, 8> name;
- INSERT_PADDING_BYTES(1);
+ u8 is_dst;
s32_le utc_offset;
};
static_assert(sizeof(CalendarAdditionalInfo) == 0x18,
@@ -41,8 +41,10 @@ struct TimeZoneRule {
};
struct SteadyClockTimePoint {
+ using SourceID = std::array<u8, 16>;
+
u64_le value;
- INSERT_PADDING_WORDS(4);
+ SourceID source_id;
};
static_assert(sizeof(SteadyClockTimePoint) == 0x18, "SteadyClockTimePoint is incorrect size");
@@ -53,6 +55,24 @@ struct SystemClockContext {
static_assert(sizeof(SystemClockContext) == 0x20,
"SystemClockContext structure has incorrect size");
+struct ClockSnapshot {
+ SystemClockContext user_clock_context;
+ SystemClockContext network_clock_context;
+ s64_le system_posix_time;
+ s64_le network_posix_time;
+ CalendarTime system_calendar_time;
+ CalendarTime network_calendar_time;
+ CalendarAdditionalInfo system_calendar_info;
+ CalendarAdditionalInfo network_calendar_info;
+ SteadyClockTimePoint steady_clock_timepoint;
+ LocationName location_name;
+ u8 clock_auto_adjustment_enabled;
+ u8 type;
+ u8 version;
+ INSERT_PADDING_BYTES(1);
+};
+static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
+
class Module final {
public:
class Interface : public ServiceFramework<Interface> {
@@ -65,6 +85,8 @@ public:
void GetStandardSteadyClock(Kernel::HLERequestContext& ctx);
void GetTimeZoneService(Kernel::HLERequestContext& ctx);
void GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx);
+ void GetClockSnapshot(Kernel::HLERequestContext& ctx);
+ void CalculateStandardUserSystemClockDifferenceByUser(Kernel::HLERequestContext& ctx);
protected:
std::shared_ptr<Module> time;
diff --git a/src/core/hle/service/usb/usb.cpp b/src/core/hle/service/usb/usb.cpp
index c489da071..58a9845fc 100644
--- a/src/core/hle/service/usb/usb.cpp
+++ b/src/core/hle/service/usb/usb.cpp
@@ -73,7 +73,7 @@ public:
{3, nullptr, "Populate"},
{4, nullptr, "PostBufferAsync"},
{5, nullptr, "GetXferReport"},
- {6, nullptr, "Unknown2"},
+ {6, nullptr, "PostBufferMultiAsync"},
{7, nullptr, "Unknown3"},
{8, nullptr, "Unknown4"},
};
@@ -132,11 +132,11 @@ public:
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "BindNoticeEvent"},
- {1, nullptr, "Unknown1"},
+ {1, nullptr, "UnbindNoticeEvent"},
{2, nullptr, "GetStatus"},
{3, nullptr, "GetNotice"},
- {4, nullptr, "Unknown2"},
- {5, nullptr, "Unknown3"},
+ {4, nullptr, "EnablePowerRequestNotice"},
+ {5, nullptr, "DisablePowerRequestNotice"},
{6, nullptr, "ReplyPowerRequest"},
};
// clang-format on
@@ -159,11 +159,11 @@ public:
private:
void GetPdSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_USB, "called");
+
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
rb.PushIpcInterface<IPdSession>();
-
- LOG_DEBUG(Service_USB, "called");
}
};
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 184537daa..a3b8cec72 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -6,9 +6,10 @@
#include <array>
#include <cstring>
#include <memory>
+#include <optional>
#include <type_traits>
#include <utility>
-#include <boost/optional.hpp>
+
#include "common/alignment.h"
#include "common/assert.h"
#include "common/common_funcs.h"
@@ -17,7 +18,9 @@
#include "common/swap.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/event.h"
+#include "core/hle/kernel/readable_event.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
#include "core/hle/service/nvflinger/nvflinger.h"
@@ -236,6 +239,22 @@ private:
Data data{};
};
+/// Represents a parcel containing one int '0' as its data
+/// Used by DetachBuffer and Disconnect
+class IGBPEmptyResponseParcel : public Parcel {
+protected:
+ void SerializeData() override {
+ Write(data);
+ }
+
+private:
+ struct Data {
+ u32_le unk_0;
+ };
+
+ Data data{};
+};
+
class IGBPSetPreallocatedBufferRequestParcel : public Parcel {
public:
explicit IGBPSetPreallocatedBufferRequestParcel(std::vector<u8> buffer)
@@ -484,16 +503,22 @@ private:
void TransactParcel(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 id = rp.Pop<u32>();
- auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
- u32 flags = rp.Pop<u32>();
- auto buffer_queue = nv_flinger->GetBufferQueue(id);
+ const u32 id = rp.Pop<u32>();
+ const auto transaction = static_cast<TransactionId>(rp.Pop<u32>());
+ const u32 flags = rp.Pop<u32>();
- LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction));
+ LOG_DEBUG(Service_VI, "called. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
+ static_cast<u32>(transaction), flags);
+
+ auto buffer_queue = nv_flinger->GetBufferQueue(id);
if (transaction == TransactionId::Connect) {
IGBPConnectRequestParcel request{ctx.ReadBuffer()};
- IGBPConnectResponseParcel response{1280, 720};
+ IGBPConnectResponseParcel response{
+ static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedWidth) *
+ Settings::values.resolution_factor),
+ static_cast<u32>(static_cast<u32>(DisplayResolution::UndockedHeight) *
+ Settings::values.resolution_factor)};
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::SetPreallocatedBuffer) {
IGBPSetPreallocatedBufferRequestParcel request{ctx.ReadBuffer()};
@@ -506,9 +531,9 @@ private:
IGBPDequeueBufferRequestParcel request{ctx.ReadBuffer()};
const u32 width{request.data.width};
const u32 height{request.data.height};
- boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+ std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
- if (slot != boost::none) {
+ if (slot) {
// Buffer is available
IGBPDequeueBufferResponseParcel response{*slot};
ctx.WriteBuffer(response.Serialize());
@@ -520,13 +545,15 @@ private:
Kernel::ThreadWakeupReason reason) {
// Repeat TransactParcel DequeueBuffer when a buffer is available
auto buffer_queue = nv_flinger->GetBufferQueue(id);
- boost::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+ std::optional<u32> slot = buffer_queue->DequeueBuffer(width, height);
+ ASSERT_MSG(slot != std::nullopt, "Could not dequeue buffer.");
+
IGBPDequeueBufferResponseParcel response{*slot};
ctx.WriteBuffer(response.Serialize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
},
- buffer_queue->GetBufferWaitEvent());
+ buffer_queue->GetWritableBufferWaitEvent());
}
} else if (transaction == TransactionId::RequestBuffer) {
IGBPRequestBufferRequestParcel request{ctx.ReadBuffer()};
@@ -553,6 +580,12 @@ private:
ctx.WriteBuffer(response.Serialize());
} else if (transaction == TransactionId::CancelBuffer) {
LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
+ } else if (transaction == TransactionId::Disconnect ||
+ transaction == TransactionId::DetachBuffer) {
+ const auto buffer = ctx.ReadBuffer();
+
+ IGBPEmptyResponseParcel response{};
+ ctx.WriteBuffer(response.Serialize());
} else {
ASSERT_MSG(false, "Unimplemented");
}
@@ -563,26 +596,27 @@ private:
void AdjustRefcount(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 id = rp.Pop<u32>();
- s32 addval = rp.PopRaw<s32>();
- u32 type = rp.Pop<u32>();
+ const u32 id = rp.Pop<u32>();
+ const s32 addval = rp.PopRaw<s32>();
+ const u32 type = rp.Pop<u32>();
LOG_WARNING(Service_VI, "(STUBBED) called id={}, addval={:08X}, type={:08X}", id, addval,
type);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetNativeHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 id = rp.Pop<u32>();
- u32 unknown = rp.Pop<u32>();
+ const u32 id = rp.Pop<u32>();
+ const u32 unknown = rp.Pop<u32>();
- auto buffer_queue = nv_flinger->GetBufferQueue(id);
+ LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
- // TODO(Subv): Find out what this actually is.
+ const auto buffer_queue = nv_flinger->GetBufferQueue(id);
- LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
+ // TODO(Subv): Find out what this actually is.
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(buffer_queue->GetBufferWaitEvent());
@@ -645,10 +679,12 @@ public:
private:
void SetLayerZ(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u64 layer_id = rp.Pop<u64>();
- u64 z_value = rp.Pop<u64>();
+ const u64 layer_id = rp.Pop<u64>();
+ const u64 z_value = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}, z_value=0x{:016X}", layer_id,
+ z_value);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -656,30 +692,36 @@ private:
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u64 layer_id = rp.Pop<u64>();
- bool visibility = rp.Pop<bool>();
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ const u64 layer_id = rp.Pop<u64>();
+ const bool visibility = rp.Pop<bool>();
+
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
visibility);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
void GetDisplayMode(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
+ static_cast<u32>(Settings::values.resolution_factor));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
+ static_cast<u32>(Settings::values.resolution_factor));
} else {
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth));
- rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedWidth) *
+ static_cast<u32>(Settings::values.resolution_factor));
+ rb.Push(static_cast<u32>(Service::VI::DisplayResolution::UndockedHeight) *
+ static_cast<u32>(Settings::values.resolution_factor));
}
- rb.PushRaw<float>(60.0f);
+ rb.PushRaw<float>(60.0f); // This wouldn't seem to be correct for 30 fps games.
rb.Push<u32>(0);
-
- LOG_DEBUG(Service_VI, "called");
}
};
@@ -761,23 +803,27 @@ public:
private:
void CloseDisplay(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u64 display = rp.Pop<u64>();
+ const u64 display = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. display=0x{:016X}", display);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void CreateManagedLayer(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u32 unknown = rp.Pop<u32>();
+ const u32 unknown = rp.Pop<u32>();
rp.Skip(1, false);
- u64 display = rp.Pop<u64>();
- u64 aruid = rp.Pop<u64>();
+ const u64 display = rp.Pop<u64>();
+ const u64 aruid = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI,
+ "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
+ unknown, display, aruid);
- u64 layer_id = nv_flinger->CreateLayer(display);
+ const u64 layer_id = nv_flinger->CreateLayer(display);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -785,10 +831,12 @@ private:
}
void AddToLayerStack(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u32 stack = rp.Pop<u32>();
- u64 layer_id = rp.Pop<u64>();
+ const u32 stack = rp.Pop<u32>();
+ const u64 layer_id = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. stack=0x{:08X}, layer_id=0x{:016X}", stack,
+ layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -796,12 +844,14 @@ private:
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u64 layer_id = rp.Pop<u64>();
- bool visibility = rp.Pop<bool>();
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ const u64 layer_id = rp.Pop<u64>();
+ const bool visibility = rp.Pop<bool>();
+
LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:X}, visibility={}", layer_id,
visibility);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
std::shared_ptr<NVFlinger::NVFlinger> nv_flinger;
@@ -847,11 +897,25 @@ private:
void OpenDisplay(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
+
IPC::RequestParser rp{ctx};
- auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
- auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
+ const auto name_buf = rp.PopRaw<std::array<char, 0x40>>();
- std::string name(name_buf.begin(), end);
+ OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()});
+ }
+
+ void OpenDefaultDisplay(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
+
+ OpenDisplayImpl(ctx, "Default");
+ }
+
+ void OpenDisplayImpl(Kernel::HLERequestContext& ctx, std::string_view name) {
+ const auto trim_pos = name.find('\0');
+
+ if (trim_pos != std::string_view::npos) {
+ name.remove_suffix(name.size() - trim_pos);
+ }
ASSERT_MSG(name == "Default", "Non-default displays aren't supported yet");
@@ -861,64 +925,85 @@ private:
}
void CloseDisplay(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u64 display_id = rp.Pop<u64>();
+ const u64 display_id = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ // This literally does nothing internally in the actual service itself,
+ // and just returns a successful result code regardless of the input.
+ void SetDisplayEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called.");
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u64 display_id = rp.Pop<u64>();
+ const u64 display_id = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
if (Settings::values.use_docked_mode) {
- rb.Push(static_cast<u64>(DisplayResolution::DockedWidth));
- rb.Push(static_cast<u64>(DisplayResolution::DockedHeight));
+ rb.Push(static_cast<u64>(DisplayResolution::DockedWidth) *
+ static_cast<u32>(Settings::values.resolution_factor));
+ rb.Push(static_cast<u64>(DisplayResolution::DockedHeight) *
+ static_cast<u32>(Settings::values.resolution_factor));
} else {
- rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth));
- rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight));
+ rb.Push(static_cast<u64>(DisplayResolution::UndockedWidth) *
+ static_cast<u32>(Settings::values.resolution_factor));
+ rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
+ static_cast<u32>(Settings::values.resolution_factor));
}
}
void SetLayerScalingMode(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u32 scaling_mode = rp.Pop<u32>();
- u64 unknown = rp.Pop<u64>();
+ const u32 scaling_mode = rp.Pop<u32>();
+ const u64 unknown = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. scaling_mode=0x{:08X}, unknown=0x{:016X}",
+ scaling_mode, unknown);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void ListDisplays(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_VI, "(STUBBED) called");
+
IPC::RequestParser rp{ctx};
DisplayInfo display_info;
+ display_info.width *= static_cast<u64>(Settings::values.resolution_factor);
+ display_info.height *= static_cast<u64>(Settings::values.resolution_factor);
ctx.WriteBuffer(&display_info, sizeof(DisplayInfo));
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
rb.Push<u64>(1);
- LOG_WARNING(Service_VI, "(STUBBED) called");
}
void OpenLayer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
IPC::RequestParser rp{ctx};
- auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
- auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
+ const auto name_buf = rp.PopRaw<std::array<u8, 0x40>>();
+ const auto end = std::find(name_buf.begin(), name_buf.end(), '\0');
+
+ const std::string display_name(name_buf.begin(), end);
- std::string display_name(name_buf.begin(), end);
+ const u64 layer_id = rp.Pop<u64>();
+ const u64 aruid = rp.Pop<u64>();
- u64 layer_id = rp.Pop<u64>();
- u64 aruid = rp.Pop<u64>();
+ LOG_DEBUG(Service_VI, "called. layer_id=0x{:016X}, aruid=0x{:016X}", layer_id, aruid);
- u64 display_id = nv_flinger->OpenDisplay(display_name);
- u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
+ const u64 display_id = nv_flinger->OpenDisplay(display_name);
+ const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb{ctx, 4};
@@ -927,17 +1012,17 @@ private:
}
void CreateStrayLayer(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_VI, "called");
-
IPC::RequestParser rp{ctx};
- u32 flags = rp.Pop<u32>();
+ const u32 flags = rp.Pop<u32>();
rp.Pop<u32>(); // padding
- u64 display_id = rp.Pop<u64>();
+ const u64 display_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_VI, "called. flags=0x{:08X}, display_id=0x{:016X}", flags, display_id);
// TODO(Subv): What's the difference between a Stray and a Managed layer?
- u64 layer_id = nv_flinger->CreateLayer(display_id);
- u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
+ const u64 layer_id = nv_flinger->CreateLayer(display_id);
+ const u32 buffer_queue_id = nv_flinger->GetBufferQueueId(display_id, layer_id);
NativeWindow native_window{buffer_queue_id};
IPC::ResponseBuilder rb{ctx, 6};
@@ -947,21 +1032,22 @@ private:
}
void DestroyStrayLayer(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
-
IPC::RequestParser rp{ctx};
- u64 layer_id = rp.Pop<u64>();
+ const u64 layer_id = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. layer_id=0x{:016X}", layer_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void GetDisplayVsyncEvent(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
IPC::RequestParser rp{ctx};
- u64 display_id = rp.Pop<u64>();
+ const u64 display_id = rp.Pop<u64>();
+
+ LOG_WARNING(Service_VI, "(STUBBED) called. display_id=0x{:016X}", display_id);
- auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
+ const auto vsync_event = nv_flinger->GetVsyncEvent(display_id);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
@@ -1030,9 +1116,9 @@ IApplicationDisplayService::IApplicationDisplayService(
"GetIndirectDisplayTransactionService"},
{1000, &IApplicationDisplayService::ListDisplays, "ListDisplays"},
{1010, &IApplicationDisplayService::OpenDisplay, "OpenDisplay"},
- {1011, nullptr, "OpenDefaultDisplay"},
+ {1011, &IApplicationDisplayService::OpenDefaultDisplay, "OpenDefaultDisplay"},
{1020, &IApplicationDisplayService::CloseDisplay, "CloseDisplay"},
- {1101, nullptr, "SetDisplayEnabled"},
+ {1101, &IApplicationDisplayService::SetDisplayEnabled, "SetDisplayEnabled"},
{1102, &IApplicationDisplayService::GetDisplayResolution, "GetDisplayResolution"},
{2020, &IApplicationDisplayService::OpenLayer, "OpenLayer"},
{2021, nullptr, "CloseLayer"},