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.h23
-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.h4
-rw-r--r--src/core/hle/kernel/handle_table.h6
-rw-r--r--src/core/hle/kernel/hle_ipc.cpp5
-rw-r--r--src/core/hle/kernel/hle_ipc.h9
-rw-r--r--src/core/hle/kernel/kernel.cpp13
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/object.cpp1
-rw-r--r--src/core/hle/kernel/object.h1
-rw-r--r--src/core/hle/kernel/process.cpp130
-rw-r--r--src/core/hle/kernel/process.h100
-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.cpp3
-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.cpp1
-rw-r--r--src/core/hle/kernel/server_session.h1
-rw-r--r--src/core/hle/kernel/shared_memory.cpp14
-rw-r--r--src/core/hle/kernel/shared_memory.h6
-rw-r--r--src/core/hle/kernel/svc.cpp334
-rw-r--r--src/core/hle/kernel/svc.h16
-rw-r--r--src/core/hle/kernel/svc_wrap.h34
-rw-r--r--src/core/hle/kernel/thread.cpp51
-rw-r--r--src/core/hle/kernel/thread.h42
-rw-r--r--src/core/hle/kernel/timer.cpp3
-rw-r--r--src/core/hle/kernel/vm_manager.cpp131
-rw-r--r--src/core/hle/kernel/vm_manager.h317
-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.h2
-rw-r--r--src/core/hle/service/am/am.cpp82
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/am/applets/applets.cpp8
-rw-r--r--src/core/hle/service/am/applets/applets.h2
-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.cpp3
-rw-r--r--src/core/hle/service/am/applets/web_browser.cpp190
-rw-r--r--src/core/hle/service/am/applets/web_browser.h44
-rw-r--r--src/core/hle/service/aoc/aoc_u.cpp12
-rw-r--r--src/core/hle/service/fatal/fatal.cpp3
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp47
-rw-r--r--src/core/hle/service/filesystem/filesystem.h6
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp28
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp52
-rw-r--r--src/core/hle/service/hid/controllers/npad.h6
-rw-r--r--src/core/hle/service/hid/hid.cpp1064
-rw-r--r--src/core/hle/service/hid/hid.h110
-rw-r--r--src/core/hle/service/ldr/ldr.cpp36
-rw-r--r--src/core/hle/service/nfp/nfp.cpp14
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp4
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp34
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.h9
-rw-r--r--src/core/hle/service/pm/pm.cpp15
-rw-r--r--src/core/hle/service/pm/pm.h7
-rw-r--r--src/core/hle/service/service.cpp31
-rw-r--r--src/core/hle/service/service.h8
-rw-r--r--src/core/hle/service/sm/sm.cpp17
-rw-r--r--src/core/hle/service/time/time.cpp34
-rw-r--r--src/core/hle/service/time/time.h12
-rw-r--r--src/core/hle/service/vi/vi.cpp303
64 files changed, 3121 insertions, 1202 deletions
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 0a7142ada..90f276ee8 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 {
@@ -217,6 +217,11 @@ private:
/// Push ///
template <>
+inline void ResponseBuilder::Push(s32 value) {
+ cmdbuf[index++] = static_cast<u32>(value);
+}
+
+template <>
inline void ResponseBuilder::Push(u32 value) {
cmdbuf[index++] = value;
}
@@ -235,6 +240,22 @@ inline void ResponseBuilder::Push(ResultCode value) {
}
template <>
+inline void ResponseBuilder::Push(s8 value) {
+ PushRaw(value);
+}
+
+template <>
+inline void ResponseBuilder::Push(s16 value) {
+ PushRaw(value);
+}
+
+template <>
+inline void ResponseBuilder::Push(s64 value) {
+ Push(static_cast<u32>(value));
+ Push(static_cast<u32>(value >> 32));
+}
+
+template <>
inline void ResponseBuilder::Push(u8 value) {
PushRaw(value);
}
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 8b58d701d..d17eb0cb6 100644
--- a/src/core/hle/kernel/errors.h
+++ b/src/core/hle/kernel/errors.h
@@ -11,6 +11,7 @@ namespace Kernel {
// Confirmed Switch kernel error codes
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};
@@ -27,9 +28,10 @@ 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_ALREADY_REGISTERED{ErrorModule::Kernel, 122};
+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.h b/src/core/hle/kernel/handle_table.h
index 6b7927fd8..89a3bc740 100644
--- a/src/core/hle/kernel/handle_table.h
+++ b/src/core/hle/kernel/handle_table.h
@@ -43,6 +43,9 @@ 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();
@@ -91,9 +94,6 @@ public:
void Clear();
private:
- /// This is the maximum limit of handles allowed per process in Horizon
- static constexpr std::size_t MAX_COUNT = 1024;
-
/// 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 61ce7d7e4..5dd855db8 100644
--- a/src/core/hle/kernel/hle_ipc.cpp
+++ b/src/core/hle/kernel/hle_ipc.cpp
@@ -22,11 +22,16 @@
#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));
diff --git a/src/core/hle/kernel/hle_ipc.h b/src/core/hle/kernel/hle_ipc.h
index e5c0610cd..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;
@@ -27,9 +25,13 @@ class Domain;
class HandleTable;
class HLERequestContext;
class Process;
+class ServerSession;
+class Thread;
class ReadableEvent;
class WritableEvent;
+enum class ThreadWakeupReason;
+
/**
* Interface implemented by HLE Session handlers.
* This can be provided to a ServerSession in order to hook into several relevant events
@@ -37,7 +39,8 @@ class WritableEvent;
*/
class SessionRequestHandler : public std::enable_shared_from_this<SessionRequestHandler> {
public:
- virtual ~SessionRequestHandler() = default;
+ SessionRequestHandler();
+ virtual ~SessionRequestHandler();
/**
* Handles a sync request from the emulated application.
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index e441c5bc6..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>
@@ -112,7 +111,7 @@ struct KernelCore::Impl {
void Shutdown() {
next_object_id = 0;
- next_process_id = 10;
+ next_process_id = Process::ProcessIDMin;
next_thread_id = 1;
process_list.clear();
@@ -153,10 +152,8 @@ 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;
@@ -242,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 ea00c89f5..58c9d108b 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -88,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/object.cpp b/src/core/hle/kernel/object.cpp
index 0ea851a74..806078638 100644
--- a/src/core/hle/kernel/object.cpp
+++ b/src/core/hle/kernel/object.cpp
@@ -32,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 f1606a204..1541b6e3c 100644
--- a/src/core/hle/kernel/object.h
+++ b/src/core/hle/kernel/object.h
@@ -36,7 +36,6 @@ enum class HandleType : u32 {
enum class ResetType {
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 211bf6686..c5aa19afa 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -20,6 +20,35 @@
#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;
@@ -28,13 +57,11 @@ 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.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;
@@ -64,82 +91,15 @@ ResultCode Process::ClearSignalState() {
return RESULT_SUCCESS;
}
-void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
+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());
-}
-
-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
- AddressMapping mapping;
- mapping.address = descriptor << 12;
- VAddr end_address = end_desc << 12;
-
- if (mapping.address < end_address) {
- mapping.size = end_address - mapping.address;
- } else {
- mapping.size = 0;
- }
+ vm_manager.Reset(metadata.GetAddressSpaceType());
- 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);
- }
- }
+ 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) {
@@ -149,13 +109,13 @@ 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();
ChangeStatus(ProcessStatus::Running);
- Kernel::SetupMainThread(kernel, entry_point, main_thread_priority, *this);
+ SetupMainThread(*this, kernel, entry_point, main_thread_priority);
}
void Process::PrepareForTermination() {
@@ -267,22 +227,6 @@ void Process::LoadModule(CodeSet module_, VAddr base_addr) {
Core::System::GetInstance().ArmInterface(3).ClearInstructionCache();
}
-ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) {
- return vm_manager.HeapAllocate(target, size, perms);
-}
-
-ResultCode Process::HeapFree(VAddr target, u32 size) {
- return vm_manager.HeapFree(target, size);
-}
-
-ResultCode Process::MirrorMemory(VAddr dst_addr, VAddr src_addr, u64 size, MemoryState state) {
- return vm_manager.MirrorMemory(dst_addr, src_addr, size, state);
-}
-
-ResultCode Process::UnmapMemory(VAddr dst_addr, VAddr /*src_addr*/, u64 size) {
- return vm_manager.UnmapRange(dst_addr, size);
-}
-
Kernel::Process::Process(KernelCore& kernel) : WaitObject{kernel} {}
Kernel::Process::~Process() {}
diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h
index bcb9ac4b8..dcc57ae9f 100644
--- a/src/core/hle/kernel/process.h
+++ b/src/core/hle/kernel/process.h
@@ -11,10 +11,9 @@
#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/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"
@@ -27,6 +26,7 @@ namespace Kernel {
class KernelCore;
class ResourceLimit;
+class Thread;
struct AddressMapping {
// Address and size must be page-aligned
@@ -42,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.
*
@@ -120,6 +102,18 @@ struct CodeSet final {
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);
@@ -162,7 +156,7 @@ public:
}
/// Gets the unique ID that identifies this particular process.
- u32 GetProcessID() const {
+ u64 GetProcessID() const {
return process_id;
}
@@ -174,19 +168,19 @@ public:
/// Gets the resource limit descriptor for this process
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 {
@@ -227,15 +221,12 @@ public:
* 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.
@@ -251,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);
@@ -259,14 +250,6 @@ 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,
- MemoryState state = MemoryState::Mapped);
-
- ResultCode UnmapMemory(VAddr dst_addr, VAddr src_addr, u64 size);
-
private:
explicit Process(KernelCore& kernel);
~Process() override;
@@ -289,30 +272,16 @@ 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;
/// The Thread Local Storage area is allocated as processes create threads,
@@ -322,6 +291,9 @@ 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.
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/readable_event.cpp b/src/core/hle/kernel/readable_event.cpp
index ba01f495c..6973e580c 100644
--- a/src/core/hle/kernel/readable_event.cpp
+++ b/src/core/hle/kernel/readable_event.cpp
@@ -46,9 +46,6 @@ ResultCode ReadableEvent::Reset() {
void ReadableEvent::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
-
- if (reset_type == ResetType::Pulse)
- signaled = false;
}
} // 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 80897f3a4..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"
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 0494581f5..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");
diff --git a/src/core/hle/kernel/shared_memory.h b/src/core/hle/kernel/shared_memory.h
index 0b48db699..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");
@@ -139,7 +139,7 @@ private:
/// Permission restrictions applied to other processes mapping the block.
MemoryPermission other_permissions{};
/// Process that created this shared memory block.
- SharedPtr<Process> owner_process;
+ Process* owner_process;
/// Address of shared memory block in the owner process if specified.
VAddr base_address = 0;
/// Name of shared memory object.
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 84df2040e..7cfecb68c 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -35,6 +35,7 @@
#include "core/hle/lock.h"
#include "core/hle/result.h"
#include "core/hle/service/service.h"
+#include "core/memory.h"
namespace Kernel {
namespace {
@@ -189,10 +190,16 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 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;
}
@@ -239,7 +246,7 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
}
const VMManager::VMAHandle iter = vm_manager.FindVMA(addr);
- if (iter == vm_manager.vma_map.end()) {
+ if (!vm_manager.IsValidHandle(iter)) {
LOG_ERROR(Kernel_SVC, "Unable to find VMA for address=0x{:016X}", addr);
return ERR_INVALID_ADDRESS_STATE;
}
@@ -253,11 +260,52 @@ static ResultCode SetMemoryPermission(VAddr addr, u64 size, u32 prot) {
return vm_manager.ReprotectRange(addr, size, converted_permissions);
}
-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 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.
@@ -265,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
@@ -281,15 +328,14 @@ 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
@@ -349,7 +395,7 @@ 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();
@@ -363,20 +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) {
- LOG_ERROR(Kernel_SVC, "Process handle does not exist, process_handle=0x{:08X}",
- process_handle);
- 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
@@ -538,6 +597,7 @@ enum class BreakType : u32 {
PostNROLoad = 4,
PreNROUnload = 5,
PostNROUnload = 6,
+ CppException = 7,
};
struct BreakReason {
@@ -610,6 +670,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"Signalling debugger, Unloaded an NRO at 0x{:016X} with size 0x{:016X}", info1,
info2);
break;
+ case BreakType::CppException:
+ LOG_CRITICAL(Debug_Emulated, "Signalling debugger. Uncaught C++ exception encountered.");
+ break;
default:
LOG_WARNING(
Debug_Emulated,
@@ -625,6 +688,9 @@ static void Break(u32 reason, u64 info1, u64 info2) {
"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();
@@ -653,8 +719,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,
@@ -685,8 +751,8 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
const auto info_id_type = static_cast<GetInfoType>(info_id);
switch (info_id_type) {
- case GetInfoType::AllowedCpuIdBitmask:
- case GetInfoType::AllowedThreadPrioBitmask:
+ case GetInfoType::AllowedCPUCoreMask:
+ case GetInfoType::AllowedThreadPriorityMask:
case GetInfoType::MapRegionBaseAddr:
case GetInfoType::MapRegionSize:
case GetInfoType::HeapRegionBaseAddr:
@@ -712,12 +778,12 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
switch (info_id_type) {
- case GetInfoType::AllowedCpuIdBitmask:
- *result = process->GetAllowedProcessorMask();
+ case GetInfoType::AllowedCPUCoreMask:
+ *result = process->GetCoreMask();
return RESULT_SUCCESS;
- case GetInfoType::AllowedThreadPrioBitmask:
- *result = process->GetAllowedThreadPriorityMask();
+ case GetInfoType::AllowedThreadPriorityMask:
+ *result = process->GetPriorityMask();
return RESULT_SUCCESS;
case GetInfoType::MapRegionBaseAddr:
@@ -877,8 +943,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id)
}
/// 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;
}
@@ -905,7 +998,7 @@ static ResultCode GetThreadContext(VAddr thread_context, Handle handle) {
if (thread == GetCurrentThread()) {
LOG_ERROR(Kernel_SVC, "The thread handle specified is the current running thread");
- return ERR_ALREADY_REGISTERED;
+ return ERR_BUSY;
}
Core::ARM_Interface::ThreadContext ctx = thread->GetContext();
@@ -1066,10 +1159,9 @@ static ResultCode UnmapSharedMemory(Handle shared_memory_handle, VAddr addr, u64
return shared_memory->Unmap(*current_process, addr);
}
-/// Query process memory
-static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_info*/,
- Handle process_handle, u64 addr) {
- LOG_TRACE(Kernel_SVC, "called process=0x{:08X} addr={:X}", process_handle, 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) {
@@ -1077,26 +1169,34 @@ static ResultCode QueryProcessMemory(MemoryInfo* memory_info, PageInfo* /*page_i
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);
- }
+
+ 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
@@ -1123,31 +1223,37 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
"threadpriority=0x{:08X}, processorid=0x{:08X} : created handle=0x{:08X}",
entry_point, arg, stack_top, priority, processor_id, *out_handle);
- if (priority > THREADPRIO_LOWEST) {
- LOG_ERROR(Kernel_SVC, "An invalid priority was specified, expected {} but got {}",
- THREADPRIO_LOWEST, priority);
- return ERR_INVALID_THREAD_PRIORITY;
- }
-
auto* const current_process = Core::CurrentProcess();
- 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,
@@ -1183,7 +1289,10 @@ static ResultCode StartThread(Handle thread_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;
}
@@ -1200,18 +1309,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
@@ -1483,9 +1612,9 @@ static ResultCode CreateTransferMemory(Handle* handle, VAddr addr, u64 size, u32
}
auto& kernel = Core::System::GetInstance().Kernel();
- auto& handle_table = Core::CurrentProcess()->GetHandleTable();
- const auto shared_mem_handle = SharedMemory::Create(
- kernel, handle_table.Get<Process>(CurrentProcess), size, perms, perms, addr);
+ 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;
@@ -1520,13 +1649,13 @@ static ResultCode SetThreadCoreMask(Handle thread_handle, u32 core, u64 mask) {
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;
}
@@ -1595,10 +1724,9 @@ static ResultCode CreateSharedMemory(Handle* handle, u64 size, u32 local_permiss
}
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;
@@ -1904,7 +2032,7 @@ 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"},
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 24aef46c9..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 {
@@ -75,7 +73,15 @@ void SvcWrap() {
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);
}
@@ -129,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)>
@@ -191,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 4ffb76818..d3984dfc4 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -12,7 +12,6 @@
#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);
}
@@ -140,6 +139,11 @@ void Thread::ResumeFromWait() {
wakeup_callback = nullptr;
+ if (activity == ThreadActivity::Paused) {
+ status = ThreadStatus::Paused;
+ return;
+ }
+
status = ThreadStatus::Ready;
ChangeScheduler();
@@ -158,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,
@@ -224,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;
}
@@ -388,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 d384d50db..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,6 +377,12 @@ public:
return affinity_mask;
}
+ ThreadActivity GetActivity() const {
+ return activity;
+ }
+
+ void SetActivity(ThreadActivity value);
+
private:
explicit Thread(KernelCore& kernel);
~Thread() override;
@@ -378,7 +391,7 @@ private:
Core::ARM_Interface::ThreadContext context{};
- u32 thread_id = 0;
+ u64 thread_id = 0;
ThreadStatus status = ThreadStatus::Dormant;
@@ -438,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/timer.cpp b/src/core/hle/kernel/timer.cpp
index 6957b16e0..2c4f50e2b 100644
--- a/src/core/hle/kernel/timer.cpp
+++ b/src/core/hle/kernel/timer.cpp
@@ -68,9 +68,6 @@ void Timer::Clear() {
void Timer::WakeupAllWaitingThreads() {
WaitObject::WakeupAllWaitingThreads();
-
- if (reset_type == ResetType::Pulse)
- signaled = false;
}
void Timer::Signal(int cycles_late) {
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp
index 100f8f6bf..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;
@@ -298,6 +303,54 @@ ResultCode VMManager::HeapFree(VAddr target, u64 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);
@@ -341,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));
}
}
@@ -568,6 +621,66 @@ 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;
diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h
index d522404fe..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?
/**
@@ -189,8 +382,28 @@ public:
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 = MemoryState::Mapped);
+ 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
@@ -281,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);
@@ -328,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;
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.h b/src/core/hle/kernel/writable_event.h
index 8fa8d68ee..c9068dd3d 100644
--- a/src/core/hle/kernel/writable_event.h
+++ b/src/core/hle/kernel/writable_event.h
@@ -4,9 +4,7 @@
#pragma once
-#include "common/common_types.h"
#include "core/hle/kernel/object.h"
-#include "core/hle/kernel/wait_object.h"
namespace Kernel {
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 3a7b6da84..d1cbe0e44 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -8,6 +8,7 @@
#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/kernel.h"
#include "core/hle/kernel/process.h"
@@ -19,8 +20,10 @@
#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/applets/web_browser.h"
#include "core/hle/service/am/idle.h"
#include "core/hle/service/am/omm.h"
#include "core/hle/service/am/spsm.h"
@@ -36,10 +39,13 @@
namespace Service::AM {
constexpr ResultCode ERR_NO_DATA_IN_CHANNEL{ErrorModule::AM, 0x2};
+constexpr ResultCode ERR_NO_MESSAGES{ErrorModule::AM, 0x3};
constexpr ResultCode ERR_SIZE_OUT_OF_BOUNDS{ErrorModule::AM, 0x1F7};
enum class AppletId : u32 {
+ ProfileSelect = 0x10,
SoftwareKeyboard = 0x11,
+ LibAppletOff = 0x17,
};
constexpr u32 POP_LAUNCH_PARAMETER_MAGIC = 0xC79497CA;
@@ -71,10 +77,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) {
@@ -454,9 +463,17 @@ void ICommonStateGetter::GetEventHandle(Kernel::HLERequestContext& ctx) {
void ICommonStateGetter::ReceiveMessage(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
+ const auto message = msg_queue->PopMessage();
IPC::ResponseBuilder rb{ctx, 3};
+
+ if (message == AppletMessageQueue::AppletMessage::NoMessage) {
+ LOG_ERROR(Service_AM, "Message queue is empty");
+ rb.Push(ERR_NO_MESSAGES);
+ rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
+ return;
+ }
rb.Push(RESULT_SUCCESS);
- rb.PushEnum<AppletMessageQueue::AppletMessage>(msg_queue->PopMessage());
+ rb.PushEnum<AppletMessageQueue::AppletMessage>(message);
}
void ICommonStateGetter::GetCurrentFocusState(Kernel::HLERequestContext& ctx) {
@@ -565,7 +582,6 @@ private:
void GetAppletStateChangedEvent(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_AM, "called");
- applet->GetBroker().SignalStateChanged();
const auto event = applet->GetBroker().GetStateChangedEvent();
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -716,10 +732,10 @@ void IStorageAccessor::Write(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
- LOG_DEBUG(Service_AM, "called, offset={}", offset);
-
const std::vector<u8> data{ctx.ReadBuffer()};
+ LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, data.size());
+
if (data.size() > backing.buffer.size() - offset) {
LOG_ERROR(Service_AM,
"offset is out of bounds, backing_buffer_sz={}, data_size={}, offset={}",
@@ -739,10 +755,10 @@ void IStorageAccessor::Read(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const u64 offset{rp.Pop<u64>()};
- LOG_DEBUG(Service_AM, "called, offset={}", offset);
-
const std::size_t size{ctx.GetWriteBufferSize()};
+ LOG_DEBUG(Service_AM, "called, offset={}, size={}", offset, size);
+
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);
@@ -773,8 +789,12 @@ 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>();
+ case AppletId::LibAppletOff:
+ return std::make_shared<Applets::WebBrowser>();
default:
LOG_ERROR(Service_AM, "Unimplemented AppletId [{:08X}]! -- Falling back to stub!",
static_cast<u32>(id));
@@ -859,8 +879,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"},
@@ -1037,6 +1057,48 @@ void IApplicationFunctions::GetPseudoDeviceId(Kernel::HLERequestContext& ctx) {
rb.Push<u64>(0);
}
+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) {
auto message_queue = std::make_shared<AppletMessageQueue>();
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 34c45fadf..b6113cfdd 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -206,6 +206,8 @@ 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);
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index 47da35537..a6064c63f 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -6,7 +6,7 @@
#include "common/assert.h"
#include "core/core.h"
#include "core/hle/kernel/readable_event.h"
-#include "core/hle/kernel/server_port.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"
@@ -16,11 +16,11 @@ namespace Service::AM::Applets {
AppletDataBroker::AppletDataBroker() {
auto& kernel = Core::System::GetInstance().Kernel();
state_changed_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:StateChangedEvent");
+ kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:StateChangedEvent");
pop_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopDataOutEvent");
+ kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopDataOutEvent");
pop_interactive_out_data_event = Kernel::WritableEvent::CreateEventPair(
- kernel, Kernel::ResetType::OneShot, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
+ kernel, Kernel::ResetType::Sticky, "ILibraryAppletAccessor:PopInteractiveDataOutEvent");
}
AppletDataBroker::~AppletDataBroker() = default;
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index b0a8913c3..37424c379 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -7,7 +7,7 @@
#include <memory>
#include <queue>
#include "common/swap.h"
-#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/object.h"
#include "core/hle/kernel/writable_event.h"
union ResultCode;
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..14e2a1fee
--- /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/profile_select.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
index 981bdec51..f255f74b5 100644
--- a/src/core/hle/service/am/applets/software_keyboard.cpp
+++ b/src/core/hle/service/am/applets/software_keyboard.cpp
@@ -146,11 +146,10 @@ void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
if (complete) {
broker.PushNormalDataFromApplet(IStorage{output_main});
+ broker.SignalStateChanged();
} else {
broker.PushInteractiveDataFromApplet(IStorage{output_sub});
}
-
- broker.SignalStateChanged();
} else {
output_main[0] = 1;
complete = true;
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp
new file mode 100644
index 000000000..9b0aa7f5f
--- /dev/null
+++ b/src/core/hle/service/am/applets/web_browser.cpp
@@ -0,0 +1,190 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <cstring>
+#include <vector>
+
+#include "common/assert.h"
+#include "common/common_funcs.h"
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "common/hex_util.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/file_sys/content_archive.h"
+#include "core/file_sys/mode.h"
+#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs.h"
+#include "core/file_sys/vfs_types.h"
+#include "core/frontend/applets/web_browser.h"
+#include "core/hle/kernel/process.h"
+#include "core/hle/service/am/applets/web_browser.h"
+#include "core/hle/service/filesystem/filesystem.h"
+#include "core/loader/loader.h"
+
+namespace Service::AM::Applets {
+
+// TODO(DarkLordZach): There are other arguments in the WebBuffer structure that are currently not
+// parsed, for example footer mode and left stick mode. Some of these are not particularly relevant,
+// but some may be worth an implementation.
+constexpr u16 WEB_ARGUMENT_URL_TYPE = 0x6;
+
+struct WebBufferHeader {
+ u16 count;
+ INSERT_PADDING_BYTES(6);
+};
+static_assert(sizeof(WebBufferHeader) == 0x8, "WebBufferHeader has incorrect size.");
+
+struct WebArgumentHeader {
+ u16 type;
+ u16 size;
+ u32 offset;
+};
+static_assert(sizeof(WebArgumentHeader) == 0x8, "WebArgumentHeader has incorrect size.");
+
+struct WebArgumentResult {
+ u32 result_code;
+ std::array<char, 0x1000> last_url;
+ u64 last_url_size;
+};
+static_assert(sizeof(WebArgumentResult) == 0x1010, "WebArgumentResult has incorrect size.");
+
+static std::vector<u8> GetArgumentDataForTagType(const std::vector<u8>& data, u16 type) {
+ WebBufferHeader header;
+ ASSERT(sizeof(WebBufferHeader) <= data.size());
+ std::memcpy(&header, data.data(), sizeof(WebBufferHeader));
+
+ u64 offset = sizeof(WebBufferHeader);
+ for (u16 i = 0; i < header.count; ++i) {
+ WebArgumentHeader arg;
+ ASSERT(offset + sizeof(WebArgumentHeader) <= data.size());
+ std::memcpy(&arg, data.data() + offset, sizeof(WebArgumentHeader));
+ offset += sizeof(WebArgumentHeader);
+
+ if (arg.type == type) {
+ std::vector<u8> out(arg.size);
+ offset += arg.offset;
+ ASSERT(offset + arg.size <= data.size());
+ std::memcpy(out.data(), data.data() + offset, out.size());
+ return out;
+ }
+
+ offset += arg.offset + arg.size;
+ }
+
+ return {};
+}
+
+static FileSys::VirtualFile GetManualRomFS() {
+ auto& loader{Core::System::GetInstance().GetAppLoader()};
+
+ FileSys::VirtualFile out;
+ if (loader.ReadManualRomFS(out) == Loader::ResultStatus::Success)
+ return out;
+
+ const auto& installed{FileSystem::GetUnionContents()};
+ const auto res = installed.GetEntry(Core::System::GetInstance().CurrentProcess()->GetTitleID(),
+ FileSys::ContentRecordType::Manual);
+
+ if (res != nullptr)
+ return res->GetRomFS();
+ return nullptr;
+}
+
+WebBrowser::WebBrowser() = default;
+
+WebBrowser::~WebBrowser() = default;
+
+void WebBrowser::Initialize() {
+ Applet::Initialize();
+
+ complete = false;
+ temporary_dir.clear();
+ filename.clear();
+ status = RESULT_SUCCESS;
+
+ const auto web_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(web_arg_storage != nullptr);
+ const auto& web_arg = web_arg_storage->GetData();
+
+ const auto url_data = GetArgumentDataForTagType(web_arg, WEB_ARGUMENT_URL_TYPE);
+ filename = Common::StringFromFixedZeroTerminatedBuffer(
+ reinterpret_cast<const char*>(url_data.data()), url_data.size());
+
+ temporary_dir = FileUtil::SanitizePath(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
+ "web_applet_manual",
+ FileUtil::DirectorySeparator::PlatformDefault);
+ FileUtil::DeleteDirRecursively(temporary_dir);
+
+ manual_romfs = GetManualRomFS();
+ if (manual_romfs == nullptr) {
+ status = ResultCode(-1);
+ LOG_ERROR(Service_AM, "Failed to find manual for current process!");
+ }
+
+ filename =
+ FileUtil::SanitizePath(temporary_dir + DIR_SEP + "html-document" + DIR_SEP + filename,
+ FileUtil::DirectorySeparator::PlatformDefault);
+}
+
+bool WebBrowser::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode WebBrowser::GetStatus() const {
+ return status;
+}
+
+void WebBrowser::ExecuteInteractive() {
+ UNIMPLEMENTED_MSG("Unexpected interactive data recieved!");
+}
+
+void WebBrowser::Execute() {
+ if (complete)
+ return;
+
+ if (status != RESULT_SUCCESS) {
+ complete = true;
+ return;
+ }
+
+ auto& frontend{Core::System::GetInstance().GetWebBrowser()};
+
+ frontend.OpenPage(filename, [this] { UnpackRomFS(); }, [this] { Finalize(); });
+}
+
+void WebBrowser::UnpackRomFS() {
+ if (unpacked)
+ return;
+
+ ASSERT(manual_romfs != nullptr);
+ const auto dir =
+ FileSys::ExtractRomFS(manual_romfs, FileSys::RomFSExtractionType::SingleDiscard);
+ const auto& vfs{Core::System::GetInstance().GetFilesystem()};
+ const auto temp_dir = vfs->CreateDirectory(temporary_dir, FileSys::Mode::ReadWrite);
+ FileSys::VfsRawCopyD(dir, temp_dir);
+
+ unpacked = true;
+}
+
+void WebBrowser::Finalize() {
+ complete = true;
+
+ WebArgumentResult out{};
+ out.result_code = 0;
+ out.last_url_size = 0;
+
+ std::vector<u8> data(sizeof(WebArgumentResult));
+ std::memcpy(data.data(), &out, sizeof(WebArgumentResult));
+
+ broker.PushNormalDataFromApplet(IStorage{data});
+ broker.SignalStateChanged();
+
+ FileUtil::DeleteDirRecursively(temporary_dir);
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h
new file mode 100644
index 000000000..b9e228fac
--- /dev/null
+++ b/src/core/hle/service/am/applets/web_browser.h
@@ -0,0 +1,44 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/file_sys/vfs_types.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Service::AM::Applets {
+
+class WebBrowser final : public Applet {
+public:
+ WebBrowser();
+ ~WebBrowser() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ // Callback to be fired when the frontend needs the manual RomFS unpacked to temporary
+ // directory. This is a blocking call and may take a while as some manuals can be up to 100MB in
+ // size. Attempting to access files at filename before invocation is likely to not work.
+ void UnpackRomFS();
+
+ // Callback to be fired when the frontend is finished browsing. This will delete the temporary
+ // manual RomFS extracted files, so ensure this is only called at actual finalization.
+ void Finalize();
+
+private:
+ bool complete = false;
+ bool unpacked = false;
+ ResultCode status = RESULT_SUCCESS;
+
+ FileSys::VirtualFile manual_romfs;
+ std::string temporary_dir;
+ std::string filename;
+};
+
+} // 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 0417fdb92..b506bc3dd 100644
--- a/src/core/hle/service/aoc/aoc_u.cpp
+++ b/src/core/hle/service/aoc/aoc_u.cpp
@@ -20,6 +20,7 @@
#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 {
@@ -76,6 +77,13 @@ void AOC_U::CountAddOnContent(Kernel::HLERequestContext& ctx) {
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); })));
@@ -96,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.
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/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index b1490e6fa..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_));
@@ -341,6 +350,44 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
return sdmc_factory->Open();
}
+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()}};
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 965414be0..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 {
@@ -48,6 +50,10 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
ResultVal<FileSys::VirtualDir> OpenSDMC();
+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();
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d2ffd5776..74c4e583b 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -45,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);
}
@@ -83,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> {
@@ -796,9 +809,18 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
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) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index d6829d0b8..04c8c35a8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -339,52 +339,6 @@ 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) {
- ControllerPadState state{};
- AnalogPosition temp_lstick_entry{};
- AnalogPosition temp_rstick_entry{};
- if (controller_type == NPadControllerType::JoyLeft) {
- state.d_down.Assign(pad_state.pad_states.d_left.Value());
- state.d_left.Assign(pad_state.pad_states.d_up.Value());
- state.d_right.Assign(pad_state.pad_states.d_down.Value());
- state.d_up.Assign(pad_state.pad_states.d_right.Value());
- state.l.Assign(pad_state.pad_states.l.Value() |
- pad_state.pad_states.left_sl.Value());
- state.r.Assign(pad_state.pad_states.r.Value() |
- pad_state.pad_states.left_sr.Value());
-
- state.zl.Assign(pad_state.pad_states.zl.Value());
- state.plus.Assign(pad_state.pad_states.minus.Value());
-
- temp_lstick_entry = pad_state.l_stick;
- temp_rstick_entry = pad_state.r_stick;
- 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.pad_states.a.Value());
- state.a.Assign(pad_state.pad_states.b.Value());
- state.b.Assign(pad_state.pad_states.y.Value());
- state.y.Assign(pad_state.pad_states.b.Value());
-
- state.l.Assign(pad_state.pad_states.l.Value() |
- pad_state.pad_states.right_sl.Value());
- state.r.Assign(pad_state.pad_states.r.Value() |
- pad_state.pad_states.right_sr.Value());
- state.zr.Assign(pad_state.pad_states.zr.Value());
- state.plus.Assign(pad_state.pad_states.plus.Value());
-
- temp_lstick_entry = pad_state.l_stick;
- temp_rstick_entry = pad_state.r_stick;
- 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.pad_states.raw = state.raw;
- pad_state.l_stick = temp_lstick_entry;
- pad_state.r_stick = temp_rstick_entry;
- }
-
libnx_entry.connection_status.raw = 0;
switch (controller_type) {
@@ -456,6 +410,8 @@ void Controller_NPad::OnUpdate(u8* data, std::size_t data_len) {
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;
+
+ press_state |= static_cast<u32>(pad_state.pad_states.raw);
}
std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
shared_memory_entries.size() * sizeof(NPadEntry));
@@ -682,6 +638,10 @@ void Controller_NPad::ClearAllControllers() {
});
}
+u32 Controller_NPad::GetAndResetPressState() {
+ return std::exchange(press_state, 0);
+}
+
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 29851f16a..106cf58c8 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -124,6 +124,10 @@ public:
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
+ // Logical OR for all buttons presses on all controllers
+ // Specifically for cheat engine and other features.
+ u32 GetAndResetPressState();
+
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -292,6 +296,8 @@ private:
bool is_connected;
};
+ u32 press_state{};
+
NPadType style{};
std::array<NPadEntry, 10> shared_memory_entries{};
std::array<
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 2ec38c726..008bf3f02 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -40,119 +40,82 @@ 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;
-enum class HidController : std::size_t {
- DebugPad,
- Touchscreen,
- Mouse,
- Keyboard,
- XPad,
- Unknown1,
- Unknown2,
- Unknown3,
- SixAxisSensor,
- NPad,
- Gesture,
-
- MaxControllers,
-};
-
-class IAppletResource final : public ServiceFramework<IAppletResource> {
-public:
- IAppletResource() : ServiceFramework("IAppletResource") {
- static const FunctionInfo functions[] = {
- {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
- };
- RegisterHandlers(functions);
-
- auto& kernel = Core::System::GetInstance().Kernel();
- shared_mem = Kernel::SharedMemory::Create(
- kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
- Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
-
- MakeController<Controller_DebugPad>(HidController::DebugPad);
- MakeController<Controller_Touchscreen>(HidController::Touchscreen);
- MakeController<Controller_Mouse>(HidController::Mouse);
- MakeController<Controller_Keyboard>(HidController::Keyboard);
- MakeController<Controller_XPad>(HidController::XPad);
- MakeController<Controller_Stubbed>(HidController::Unknown1);
- MakeController<Controller_Stubbed>(HidController::Unknown2);
- MakeController<Controller_Stubbed>(HidController::Unknown3);
- MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
- MakeController<Controller_NPad>(HidController::NPad);
- MakeController<Controller_Gesture>(HidController::Gesture);
-
- // Homebrew doesn't try to activate some controllers, so we activate them by default
- GetController<Controller_NPad>(HidController::NPad).ActivateController();
- GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
-
- GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
- GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
- GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
-
- // Register update callbacks
- pad_update_event = CoreTiming::RegisterEvent(
- "HID::UpdatePadCallback",
- [this](u64 userdata, int cycles_late) { UpdateControllers(userdata, cycles_late); });
-
- // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
-
- CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
-
- ReloadInputDevices();
- }
-
- void ActivateController(HidController controller) {
- controllers[static_cast<size_t>(controller)]->ActivateController();
- }
- void DeactivateController(HidController controller) {
- controllers[static_cast<size_t>(controller)]->DeactivateController();
- }
+IAppletResource::IAppletResource() : ServiceFramework("IAppletResource") {
+ static const FunctionInfo functions[] = {
+ {0, &IAppletResource::GetSharedMemoryHandle, "GetSharedMemoryHandle"},
+ };
+ RegisterHandlers(functions);
+
+ auto& kernel = Core::System::GetInstance().Kernel();
+ shared_mem = Kernel::SharedMemory::Create(
+ kernel, nullptr, SHARED_MEMORY_SIZE, Kernel::MemoryPermission::ReadWrite,
+ Kernel::MemoryPermission::Read, 0, Kernel::MemoryRegion::BASE, "HID:SharedMemory");
+
+ MakeController<Controller_DebugPad>(HidController::DebugPad);
+ MakeController<Controller_Touchscreen>(HidController::Touchscreen);
+ MakeController<Controller_Mouse>(HidController::Mouse);
+ MakeController<Controller_Keyboard>(HidController::Keyboard);
+ MakeController<Controller_XPad>(HidController::XPad);
+ MakeController<Controller_Stubbed>(HidController::Unknown1);
+ MakeController<Controller_Stubbed>(HidController::Unknown2);
+ MakeController<Controller_Stubbed>(HidController::Unknown3);
+ MakeController<Controller_Stubbed>(HidController::SixAxisSensor);
+ MakeController<Controller_NPad>(HidController::NPad);
+ MakeController<Controller_Gesture>(HidController::Gesture);
+
+ // Homebrew doesn't try to activate some controllers, so we activate them by default
+ GetController<Controller_NPad>(HidController::NPad).ActivateController();
+ GetController<Controller_Touchscreen>(HidController::Touchscreen).ActivateController();
+
+ GetController<Controller_Stubbed>(HidController::Unknown1).SetCommonHeaderOffset(0x4c00);
+ GetController<Controller_Stubbed>(HidController::Unknown2).SetCommonHeaderOffset(0x4e00);
+ GetController<Controller_Stubbed>(HidController::Unknown3).SetCommonHeaderOffset(0x5000);
+
+ // Register update callbacks
+ pad_update_event =
+ CoreTiming::RegisterEvent("HID::UpdatePadCallback", [this](u64 userdata, int cycles_late) {
+ UpdateControllers(userdata, cycles_late);
+ });
+
+ // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
+
+ CoreTiming::ScheduleEvent(pad_update_ticks, pad_update_event);
+
+ ReloadInputDevices();
+}
- template <typename T>
- void MakeController(HidController controller) {
- controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
- }
+void IAppletResource::ActivateController(HidController controller) {
+ controllers[static_cast<size_t>(controller)]->ActivateController();
+}
- template <typename T>
- T& GetController(HidController controller) {
- return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
- }
+void IAppletResource::DeactivateController(HidController controller) {
+ controllers[static_cast<size_t>(controller)]->DeactivateController();
+}
- ~IAppletResource() {
- CoreTiming::UnscheduleEvent(pad_update_event, 0);
- }
+IAppletResource ::~IAppletResource() {
+ CoreTiming::UnscheduleEvent(pad_update_event, 0);
+}
-private:
- void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void IAppletResource::GetSharedMemoryHandle(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(shared_mem);
- }
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(shared_mem);
+}
- void UpdateControllers(u64 userdata, int cycles_late) {
- const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
- for (const auto& controller : controllers) {
- if (should_reload) {
- controller->OnLoadInputDevices();
- }
- controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+void IAppletResource::UpdateControllers(u64 userdata, int cycles_late) {
+ const bool should_reload = Settings::values.is_device_reload_pending.exchange(false);
+ for (const auto& controller : controllers) {
+ if (should_reload) {
+ controller->OnLoadInputDevices();
}
-
- CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
+ controller->OnUpdate(shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
}
- // Handle to shared memory region designated to HID service
- Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
-
- // CoreTiming update events
- CoreTiming::EventType* pad_update_event;
-
- std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
- controllers{};
-};
+ CoreTiming::ScheduleEvent(pad_update_ticks - cycles_late, pad_update_event);
+}
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
@@ -172,480 +135,597 @@ private:
}
};
-class Hid final : public ServiceFramework<Hid> {
-public:
- Hid() : ServiceFramework("hid") {
- // clang-format off
- static const FunctionInfo functions[] = {
- {0, &Hid::CreateAppletResource, "CreateAppletResource"},
- {1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
- {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
- {21, &Hid::ActivateMouse, "ActivateMouse"},
- {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
- {32, nullptr, "SendKeyboardLockKeyEvent"},
- {40, nullptr, "AcquireXpadIdEventHandle"},
- {41, nullptr, "ReleaseXpadIdEventHandle"},
- {51, &Hid::ActivateXpad, "ActivateXpad"},
- {55, nullptr, "GetXpadIds"},
- {56, nullptr, "ActivateJoyXpad"},
- {58, nullptr, "GetJoyXpadLifoHandle"},
- {59, nullptr, "GetJoyXpadIds"},
- {60, nullptr, "ActivateSixAxisSensor"},
- {61, nullptr, "DeactivateSixAxisSensor"},
- {62, nullptr, "GetSixAxisSensorLifoHandle"},
- {63, nullptr, "ActivateJoySixAxisSensor"},
- {64, nullptr, "DeactivateJoySixAxisSensor"},
- {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
- {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
- {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
- {68, nullptr, "IsSixAxisSensorFusionEnabled"},
- {69, nullptr, "EnableSixAxisSensorFusion"},
- {70, nullptr, "SetSixAxisSensorFusionParameters"},
- {71, nullptr, "GetSixAxisSensorFusionParameters"},
- {72, nullptr, "ResetSixAxisSensorFusionParameters"},
- {73, nullptr, "SetAccelerometerParameters"},
- {74, nullptr, "GetAccelerometerParameters"},
- {75, nullptr, "ResetAccelerometerParameters"},
- {76, nullptr, "SetAccelerometerPlayMode"},
- {77, nullptr, "GetAccelerometerPlayMode"},
- {78, nullptr, "ResetAccelerometerPlayMode"},
- {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
- {80, nullptr, "GetGyroscopeZeroDriftMode"},
- {81, nullptr, "ResetGyroscopeZeroDriftMode"},
- {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
- {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
- {91, &Hid::ActivateGesture, "ActivateGesture"},
- {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
- {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
- {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
- {103, &Hid::ActivateNpad, "ActivateNpad"},
- {104, nullptr, "DeactivateNpad"},
- {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
- {107, &Hid::DisconnectNpad, "DisconnectNpad"},
- {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
- {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
- {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
- {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
- {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
- {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"},
- {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
- {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
- {126, nullptr, "StartLrAssignmentMode"},
- {127, nullptr, "StopLrAssignmentMode"},
- {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
- {129, nullptr, "GetNpadHandheldActivationMode"},
- {130, nullptr, "SwapNpadAssignment"},
- {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
- {132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
- {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
- {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
- {201, &Hid::SendVibrationValue, "SendVibrationValue"},
- {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
- {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
- {204, nullptr, "PermitVibration"},
- {205, nullptr, "IsVibrationPermitted"},
- {206, &Hid::SendVibrationValues, "SendVibrationValues"},
- {207, nullptr, "SendVibrationGcErmCommand"},
- {208, nullptr, "GetActualVibrationGcErmCommand"},
- {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
- {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
- {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
- {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
- {302, nullptr, "StopConsoleSixAxisSensor"},
- {303, nullptr, "ActivateSevenSixAxisSensor"},
- {304, nullptr, "StartSevenSixAxisSensor"},
- {305, nullptr, "StopSevenSixAxisSensor"},
- {306, nullptr, "InitializeSevenSixAxisSensor"},
- {307, nullptr, "FinalizeSevenSixAxisSensor"},
- {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
- {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
- {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
- {400, nullptr, "IsUsbFullKeyControllerEnabled"},
- {401, nullptr, "EnableUsbFullKeyController"},
- {402, nullptr, "IsUsbFullKeyControllerConnected"},
- {403, nullptr, "HasBattery"},
- {404, nullptr, "HasLeftRightBattery"},
- {405, nullptr, "GetNpadInterfaceType"},
- {406, nullptr, "GetNpadLeftRightInterfaceType"},
- {500, nullptr, "GetPalmaConnectionHandle"},
- {501, nullptr, "InitializePalma"},
- {502, nullptr, "AcquirePalmaOperationCompleteEvent"},
- {503, nullptr, "GetPalmaOperationInfo"},
- {504, nullptr, "PlayPalmaActivity"},
- {505, nullptr, "SetPalmaFrModeType"},
- {506, nullptr, "ReadPalmaStep"},
- {507, nullptr, "EnablePalmaStep"},
- {508, nullptr, "ResetPalmaStep"},
- {509, nullptr, "ReadPalmaApplicationSection"},
- {510, nullptr, "WritePalmaApplicationSection"},
- {511, nullptr, "ReadPalmaUniqueCode"},
- {512, nullptr, "SetPalmaUniqueCodeInvalid"},
- {513, nullptr, "WritePalmaActivityEntry"},
- {514, nullptr, "WritePalmaRgbLedPatternEntry"},
- {515, nullptr, "WritePalmaWaveEntry"},
- {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
- {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
- {518, nullptr, "SuspendPalmaFeature"},
- {519, nullptr, "GetPalmaOperationResult"},
- {520, nullptr, "ReadPalmaPlayLog"},
- {521, nullptr, "ResetPalmaPlayLog"},
- {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
- {523, nullptr, "SetIsPalmaPairedConnectable"},
- {524, nullptr, "PairPalma"},
- {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
- {1000, nullptr, "SetNpadCommunicationMode"},
- {1001, nullptr, "GetNpadCommunicationMode"},
- };
- // clang-format on
-
- RegisterHandlers(functions);
+std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
+ if (applet_resource == nullptr) {
+ applet_resource = std::make_shared<IAppletResource>();
}
- ~Hid() = default;
-private:
- std::shared_ptr<IAppletResource> applet_resource;
+ return applet_resource;
+}
- void CreateAppletResource(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+Hid::Hid() : ServiceFramework("hid") {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &Hid::CreateAppletResource, "CreateAppletResource"},
+ {1, &Hid::ActivateDebugPad, "ActivateDebugPad"},
+ {11, &Hid::ActivateTouchScreen, "ActivateTouchScreen"},
+ {21, &Hid::ActivateMouse, "ActivateMouse"},
+ {31, &Hid::ActivateKeyboard, "ActivateKeyboard"},
+ {32, nullptr, "SendKeyboardLockKeyEvent"},
+ {40, nullptr, "AcquireXpadIdEventHandle"},
+ {41, nullptr, "ReleaseXpadIdEventHandle"},
+ {51, &Hid::ActivateXpad, "ActivateXpad"},
+ {55, nullptr, "GetXpadIds"},
+ {56, nullptr, "ActivateJoyXpad"},
+ {58, nullptr, "GetJoyXpadLifoHandle"},
+ {59, nullptr, "GetJoyXpadIds"},
+ {60, nullptr, "ActivateSixAxisSensor"},
+ {61, nullptr, "DeactivateSixAxisSensor"},
+ {62, nullptr, "GetSixAxisSensorLifoHandle"},
+ {63, nullptr, "ActivateJoySixAxisSensor"},
+ {64, nullptr, "DeactivateJoySixAxisSensor"},
+ {65, nullptr, "GetJoySixAxisSensorLifoHandle"},
+ {66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
+ {67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
+ {68, nullptr, "IsSixAxisSensorFusionEnabled"},
+ {69, nullptr, "EnableSixAxisSensorFusion"},
+ {70, nullptr, "SetSixAxisSensorFusionParameters"},
+ {71, nullptr, "GetSixAxisSensorFusionParameters"},
+ {72, nullptr, "ResetSixAxisSensorFusionParameters"},
+ {73, nullptr, "SetAccelerometerParameters"},
+ {74, nullptr, "GetAccelerometerParameters"},
+ {75, nullptr, "ResetAccelerometerParameters"},
+ {76, nullptr, "SetAccelerometerPlayMode"},
+ {77, nullptr, "GetAccelerometerPlayMode"},
+ {78, nullptr, "ResetAccelerometerPlayMode"},
+ {79, &Hid::SetGyroscopeZeroDriftMode, "SetGyroscopeZeroDriftMode"},
+ {80, nullptr, "GetGyroscopeZeroDriftMode"},
+ {81, nullptr, "ResetGyroscopeZeroDriftMode"},
+ {82, &Hid::IsSixAxisSensorAtRest, "IsSixAxisSensorAtRest"},
+ {83, nullptr, "IsFirmwareUpdateAvailableForSixAxisSensor"},
+ {91, &Hid::ActivateGesture, "ActivateGesture"},
+ {100, &Hid::SetSupportedNpadStyleSet, "SetSupportedNpadStyleSet"},
+ {101, &Hid::GetSupportedNpadStyleSet, "GetSupportedNpadStyleSet"},
+ {102, &Hid::SetSupportedNpadIdType, "SetSupportedNpadIdType"},
+ {103, &Hid::ActivateNpad, "ActivateNpad"},
+ {104, nullptr, "DeactivateNpad"},
+ {106, &Hid::AcquireNpadStyleSetUpdateEventHandle, "AcquireNpadStyleSetUpdateEventHandle"},
+ {107, &Hid::DisconnectNpad, "DisconnectNpad"},
+ {108, &Hid::GetPlayerLedPattern, "GetPlayerLedPattern"},
+ {109, &Hid::ActivateNpadWithRevision, "ActivateNpadWithRevision"},
+ {120, &Hid::SetNpadJoyHoldType, "SetNpadJoyHoldType"},
+ {121, &Hid::GetNpadJoyHoldType, "GetNpadJoyHoldType"},
+ {122, &Hid::SetNpadJoyAssignmentModeSingleByDefault, "SetNpadJoyAssignmentModeSingleByDefault"},
+ {123, nullptr, "SetNpadJoyAssignmentModeSingleByDefault"},
+ {124, &Hid::SetNpadJoyAssignmentModeDual, "SetNpadJoyAssignmentModeDual"},
+ {125, &Hid::MergeSingleJoyAsDualJoy, "MergeSingleJoyAsDualJoy"},
+ {126, nullptr, "StartLrAssignmentMode"},
+ {127, nullptr, "StopLrAssignmentMode"},
+ {128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
+ {129, nullptr, "GetNpadHandheldActivationMode"},
+ {130, nullptr, "SwapNpadAssignment"},
+ {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
+ {132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
+ {133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
+ {200, &Hid::GetVibrationDeviceInfo, "GetVibrationDeviceInfo"},
+ {201, &Hid::SendVibrationValue, "SendVibrationValue"},
+ {202, &Hid::GetActualVibrationValue, "GetActualVibrationValue"},
+ {203, &Hid::CreateActiveVibrationDeviceList, "CreateActiveVibrationDeviceList"},
+ {204, nullptr, "PermitVibration"},
+ {205, nullptr, "IsVibrationPermitted"},
+ {206, &Hid::SendVibrationValues, "SendVibrationValues"},
+ {207, nullptr, "SendVibrationGcErmCommand"},
+ {208, nullptr, "GetActualVibrationGcErmCommand"},
+ {209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
+ {210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
+ {300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
+ {301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
+ {302, nullptr, "StopConsoleSixAxisSensor"},
+ {303, nullptr, "ActivateSevenSixAxisSensor"},
+ {304, nullptr, "StartSevenSixAxisSensor"},
+ {305, nullptr, "StopSevenSixAxisSensor"},
+ {306, nullptr, "InitializeSevenSixAxisSensor"},
+ {307, nullptr, "FinalizeSevenSixAxisSensor"},
+ {308, nullptr, "SetSevenSixAxisSensorFusionStrength"},
+ {309, nullptr, "GetSevenSixAxisSensorFusionStrength"},
+ {310, nullptr, "ResetSevenSixAxisSensorTimestamp"},
+ {400, nullptr, "IsUsbFullKeyControllerEnabled"},
+ {401, nullptr, "EnableUsbFullKeyController"},
+ {402, nullptr, "IsUsbFullKeyControllerConnected"},
+ {403, nullptr, "HasBattery"},
+ {404, nullptr, "HasLeftRightBattery"},
+ {405, nullptr, "GetNpadInterfaceType"},
+ {406, nullptr, "GetNpadLeftRightInterfaceType"},
+ {500, nullptr, "GetPalmaConnectionHandle"},
+ {501, nullptr, "InitializePalma"},
+ {502, nullptr, "AcquirePalmaOperationCompleteEvent"},
+ {503, nullptr, "GetPalmaOperationInfo"},
+ {504, nullptr, "PlayPalmaActivity"},
+ {505, nullptr, "SetPalmaFrModeType"},
+ {506, nullptr, "ReadPalmaStep"},
+ {507, nullptr, "EnablePalmaStep"},
+ {508, nullptr, "ResetPalmaStep"},
+ {509, nullptr, "ReadPalmaApplicationSection"},
+ {510, nullptr, "WritePalmaApplicationSection"},
+ {511, nullptr, "ReadPalmaUniqueCode"},
+ {512, nullptr, "SetPalmaUniqueCodeInvalid"},
+ {513, nullptr, "WritePalmaActivityEntry"},
+ {514, nullptr, "WritePalmaRgbLedPatternEntry"},
+ {515, nullptr, "WritePalmaWaveEntry"},
+ {516, nullptr, "SetPalmaDataBaseIdentificationVersion"},
+ {517, nullptr, "GetPalmaDataBaseIdentificationVersion"},
+ {518, nullptr, "SuspendPalmaFeature"},
+ {519, nullptr, "GetPalmaOperationResult"},
+ {520, nullptr, "ReadPalmaPlayLog"},
+ {521, nullptr, "ResetPalmaPlayLog"},
+ {522, &Hid::SetIsPalmaAllConnectable, "SetIsPalmaAllConnectable"},
+ {523, nullptr, "SetIsPalmaPairedConnectable"},
+ {524, nullptr, "PairPalma"},
+ {525, &Hid::SetPalmaBoostMode, "SetPalmaBoostMode"},
+ {1000, nullptr, "SetNpadCommunicationMode"},
+ {1001, nullptr, "GetNpadCommunicationMode"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
- if (applet_resource == nullptr) {
- applet_resource = std::make_shared<IAppletResource>();
- }
+Hid::~Hid() = default;
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IAppletResource>(applet_resource);
- }
+void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- void ActivateXpad(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::XPad);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ if (applet_resource == nullptr) {
+ applet_resource = std::make_shared<IAppletResource>();
}
- void ActivateDebugPad(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IAppletResource>(applet_resource);
+}
- applet_resource->ActivateController(HidController::DebugPad);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::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>()};
- void ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id,
+ applet_resource_user_id);
- applet_resource->ActivateController(HidController::Touchscreen);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ applet_resource->ActivateController(HidController::XPad);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- void ActivateMouse(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->ActivateController(HidController::Mouse);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- void ActivateKeyboard(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ applet_resource->ActivateController(HidController::DebugPad);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- applet_resource->ActivateController(HidController::Keyboard);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- void ActivateGesture(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::Gesture);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ applet_resource->ActivateController(HidController::Touchscreen);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
- // Should have no effect with how our npad sets up the data
- LOG_DEBUG(Service_HID, "called");
+void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- applet_resource->ActivateController(HidController::NPad);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- void StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto handle = rp.PopRaw<u32>();
- LOG_WARNING(Service_HID, "(STUBBED) called with handle={}", handle);
+ applet_resource->ActivateController(HidController::Mouse);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ 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::Keyboard);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto unknown{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- // TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
- rb.Push(true);
- }
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
+ applet_resource_user_id);
- void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto supported_styleset = rp.PopRaw<u32>();
- LOG_DEBUG(Service_HID, "called with supported_styleset={}", supported_styleset);
+ applet_resource->ActivateController(HidController::Gesture);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSupportedStyleSet({supported_styleset});
+void Hid::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>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
+ applet_resource_user_id);
- void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ applet_resource->ActivateController(HidController::NPad);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto handle{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(controller.GetSupportedStyleSet().raw);
- }
+ LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
+ applet_resource_user_id);
- void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::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>()};
- void ActivateNpad(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ 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);
- applet_resource->ActivateController(HidController::NPad);
- }
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
- LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
+void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto handle{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetStyleSetChangedEvent());
- }
+ LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
+ applet_resource_user_id);
- void DisconnectNpad(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
- LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ // TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
+ rb.Push(true);
+}
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .DisconnectNPad(npad_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto supported_styleset{rp.Pop<u32>()};
- void GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
- LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
+ LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetLedPattern(npad_id)
- .raw);
- }
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedStyleSet({supported_styleset});
- void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto hold_type = rp.PopRaw<u64>();
- LOG_DEBUG(Service_HID, "called with hold_type={}", hold_type);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
+void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- 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()));
- }
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(controller.GetSupportedStyleSet().raw);
+}
- void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto npad_id = rp.PopRaw<u32>();
- LOG_WARNING(Service_HID, "(STUBBED) called with npad_id={}", npad_id);
+void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetVibrationEnabled(true);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- void EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetVibrationEnabled(false);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ applet_resource->ActivateController(HidController::NPad);
+}
- 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>();
- LOG_DEBUG(Service_HID, "called with controller_id={}", controller_id);
+void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto unknown{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id,
+ applet_resource_user_id, unknown);
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .VibrateController({controller_id}, {vibration_values});
- }
+ IPC::ResponseBuilder rb{ctx, 2, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetStyleSetChangedEvent());
+}
- void SendVibrationValues(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto controllers = ctx.ReadBuffer(0);
- const auto vibrations = ctx.ReadBuffer(1);
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
+ applet_resource_user_id);
- std::vector<u32> controller_list(controllers.size() / sizeof(u32));
- std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
- sizeof(Controller_NPad::Vibration));
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).DisconnectNPad(npad_id);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- std::memcpy(controller_list.data(), controllers.data(), controllers.size());
- std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
- std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
- [](u32 controller_id) { return controller_id - 3; });
+void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.Pop<u32>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .VibrateController(controller_list, vibration_list);
+ LOG_DEBUG(Service_HID, "called, npad_id={}", npad_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLedPattern(npad_id)
+ .raw);
+}
- void GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto hold_type{rp.Pop<u64>()};
- 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, applet_resource_user_id={}, hold_type={}",
+ applet_resource_user_id, hold_type);
- void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- const auto npad_id = rp.PopRaw<u32>();
- LOG_DEBUG(Service_HID, "called with npad_id={}", npad_id);
+ auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
+ controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
- 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);
+}
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ 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()));
+}
- void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
- IPC::RequestParser rp{ctx};
- auto mode = rp.PopRaw<u32>();
- LOG_WARNING(Service_HID, "(STUBBED) called with mode={}", mode);
+void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto npad_id{rp.Pop<u32>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
+ applet_resource_user_id);
- void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(1);
- rb.Push<u32>(0);
- }
+void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- IPC::ResponseBuilder rb{ctx, 2, 0, 1};
- rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IActiveVibrationDeviceList>();
- }
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(true);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(false);
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
- void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ 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>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id,
+ applet_resource_user_id);
- void StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .VibrateController({controller_id}, {vibration_values});
+}
- void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- void SetPalmaBoostMode(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ const auto controllers = ctx.ReadBuffer(0);
+ const auto vibrations = ctx.ReadBuffer(1);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
- }
-};
+ std::vector<u32> controller_list(controllers.size() / sizeof(u32));
+ std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
+ sizeof(Controller_NPad::Vibration));
+
+ std::memcpy(controller_list.data(), controllers.data(), controllers.size());
+ std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
+ std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
+ [](u32 controller_id) { return controller_id - 3; });
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .VibrateController(controller_list, vibration_list);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::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());
+}
+
+void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ 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);
+}
+
+void Hid::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);
+}
+
+void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ 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);
+}
+
+void Hid::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);
+}
+
+void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_HID, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IActiveVibrationDeviceList>();
+}
+
+void Hid::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);
+}
+
+void Hid::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);
+}
+
+void Hid::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 Hid::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 Hid::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);
+}
class HidDbg final : public ServiceFramework<HidDbg> {
public:
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index 773035460..eca27c056 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -4,12 +4,122 @@
#pragma once
+#include "controllers/controller_base.h"
+#include "core/hle/service/service.h"
+
+namespace CoreTiming {
+struct EventType;
+}
+
+namespace Kernel {
+class SharedMemory;
+}
+
namespace SM {
class ServiceManager;
}
namespace Service::HID {
+enum class HidController : std::size_t {
+ DebugPad,
+ Touchscreen,
+ Mouse,
+ Keyboard,
+ XPad,
+ Unknown1,
+ Unknown2,
+ Unknown3,
+ SixAxisSensor,
+ NPad,
+ Gesture,
+
+ MaxControllers,
+};
+
+class IAppletResource final : public ServiceFramework<IAppletResource> {
+public:
+ IAppletResource();
+ ~IAppletResource() override;
+
+ void ActivateController(HidController controller);
+ void DeactivateController(HidController controller);
+
+ template <typename T>
+ T& GetController(HidController controller) {
+ return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
+ }
+
+ template <typename T>
+ const T& GetController(HidController controller) const {
+ return static_cast<T&>(*controllers[static_cast<size_t>(controller)]);
+ }
+
+private:
+ template <typename T>
+ void MakeController(HidController controller) {
+ controllers[static_cast<std::size_t>(controller)] = std::make_unique<T>();
+ }
+
+ void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
+ void UpdateControllers(u64 userdata, int cycles_late);
+
+ Kernel::SharedPtr<Kernel::SharedMemory> shared_mem;
+
+ CoreTiming::EventType* pad_update_event;
+
+ std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
+ controllers{};
+};
+
+class Hid final : public ServiceFramework<Hid> {
+public:
+ Hid();
+ ~Hid() override;
+
+ std::shared_ptr<IAppletResource> GetAppletResource();
+
+private:
+ void CreateAppletResource(Kernel::HLERequestContext& ctx);
+ void ActivateXpad(Kernel::HLERequestContext& ctx);
+ void ActivateDebugPad(Kernel::HLERequestContext& ctx);
+ void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
+ void ActivateMouse(Kernel::HLERequestContext& ctx);
+ void ActivateKeyboard(Kernel::HLERequestContext& ctx);
+ void ActivateGesture(Kernel::HLERequestContext& ctx);
+ void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
+ void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
+ void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
+ void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
+ void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
+ void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
+ void ActivateNpad(Kernel::HLERequestContext& ctx);
+ void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
+ void DisconnectNpad(Kernel::HLERequestContext& ctx);
+ void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
+ void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
+ void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
+ void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
+ void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void SendVibrationValue(Kernel::HLERequestContext& ctx);
+ void SendVibrationValues(Kernel::HLERequestContext& ctx);
+ void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
+ void SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx);
+ void MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx);
+ void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
+ void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
+ void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
+ void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
+ void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
+
+ std::shared_ptr<IAppletResource> applet_resource;
+};
+
/// Reload input devices. Used when input configuration changed
void ReloadInputDevices();
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index 453d90a22..9df7ac50f 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -318,14 +318,18 @@ public:
return;
}
- ASSERT(process->MirrorMemory(*map_address, nro_addr, nro_size,
- Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
- ASSERT(process->UnmapMemory(nro_addr, 0, nro_size) == RESULT_SUCCESS);
+ 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(process->MirrorMemory(*map_address + nro_size, bss_addr, bss_size,
- Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
- ASSERT(process->UnmapMemory(bss_addr, 0, bss_size) == RESULT_SUCCESS);
+ 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());
}
vm_manager.ReprotectRange(*map_address, header.text_size,
@@ -380,13 +384,14 @@ public:
return;
}
- auto* process = Core::CurrentProcess();
- auto& vm_manager = process->VMManager();
+ auto& vm_manager = Core::CurrentProcess()->VMManager();
const auto& nro_size = iter->second.size;
- ASSERT(process->MirrorMemory(heap_addr, mapped_addr, nro_size,
- Kernel::MemoryState::ModuleCodeStatic) == RESULT_SUCCESS);
- ASSERT(process->UnmapMemory(mapped_addr, 0, nro_size) == RESULT_SUCCESS);
+ 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();
@@ -408,13 +413,13 @@ private:
using SHA256Hash = std::array<u8, 0x20>;
struct NROHeader {
- u32_le entrypoint_insn;
+ INSERT_PADDING_WORDS(1);
u32_le mod_offset;
INSERT_PADDING_WORDS(2);
u32_le magic;
- INSERT_PADDING_WORDS(1);
+ u32_le version;
u32_le nro_size;
- INSERT_PADDING_WORDS(1);
+ u32_le flags;
u32_le text_offset;
u32_le text_size;
u32_le ro_offset;
@@ -430,9 +435,10 @@ private:
struct NRRHeader {
u32_le magic;
- INSERT_PADDING_BYTES(0x1C);
+ 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;
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index d5df112a0..1c4482e47 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -9,9 +9,9 @@
#include "core/hle/ipc_helpers.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"
@@ -20,7 +20,8 @@ namespace Service::NFP {
namespace ErrCodes {
constexpr ResultCode ERR_TAG_FAILED(ErrorModule::NFP,
-1); // TODO(ogniK): Find the actual error code
-}
+constexpr ResultCode ERR_NO_APPLICATION_AREA(ErrorModule::NFP, 152);
+} // namespace ErrCodes
Module::Interface::Interface(std::shared_ptr<Module> module, const char* name)
: ServiceFramework(name), module(std::move(module)) {
@@ -292,10 +293,9 @@ private:
}
void OpenApplicationArea(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NFP, "called");
- // We don't need to worry about this since we can just open the file
+ LOG_WARNING(Service_NFP, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
+ rb.Push(ErrCodes::ERR_NO_APPLICATION_AREA);
}
void GetApplicationAreaSize(Kernel::HLERequestContext& ctx) {
@@ -317,8 +317,8 @@ 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::EventPair deactivate_event;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
index 3bfce0110..0a650f36c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -137,6 +137,10 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
}
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();
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index 05af2d593..6db2cce41 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -5,7 +5,6 @@
#include <algorithm>
#include <optional>
-#include "common/alignment.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/microprofile.h"
@@ -22,7 +21,6 @@
#include "core/hle/service/nvflinger/nvflinger.h"
#include "core/perf_stats.h"
#include "video_core/renderer_base.h"
-#include "video_core/video_core.h"
namespace Service::NVFlinger {
@@ -30,12 +28,6 @@ constexpr std::size_t SCREEN_REFRESH_RATE = 60;
constexpr u64 frame_ticks = static_cast<u64>(CoreTiming::BASE_CLOCK_RATE / SCREEN_REFRESH_RATE);
NVFlinger::NVFlinger() {
- // Add the different displays to the list of displays.
- displays.emplace_back(0, "Default");
- displays.emplace_back(1, "External");
- displays.emplace_back(2, "Edid");
- displays.emplace_back(3, "Internal");
-
// Schedule the screen composition events
composition_event =
CoreTiming::RegisterEvent("ScreenComposition", [this](u64 userdata, int cycles_late) {
@@ -55,13 +47,13 @@ void NVFlinger::SetNVDrvInstance(std::shared_ptr<Nvidia::Module> instance) {
}
u64 NVFlinger::OpenDisplay(std::string_view name) {
- LOG_WARNING(Service, "Opening display {}", name);
+ LOG_DEBUG(Service, "Opening \"{}\" display", name);
// TODO(Subv): Currently we only support the Default display.
ASSERT(name == "Default");
- auto itr = std::find_if(displays.begin(), displays.end(),
- [&](const Display& display) { return display.name == name; });
+ const auto itr = std::find_if(displays.begin(), displays.end(),
+ [&](const Display& display) { return display.name == name; });
ASSERT(itr != displays.end());
@@ -73,8 +65,8 @@ u64 NVFlinger::CreateLayer(u64 display_id) {
ASSERT_MSG(display.layers.empty(), "Only one layer is supported per display at the moment");
- u64 layer_id = next_layer_id++;
- u32 buffer_queue_id = next_buffer_queue_id++;
+ const u64 layer_id = next_layer_id++;
+ const u32 buffer_queue_id = next_buffer_queue_id++;
auto buffer_queue = std::make_shared<BufferQueue>(buffer_queue_id, layer_id);
display.layers.emplace_back(layer_id, buffer_queue);
buffer_queues.emplace_back(std::move(buffer_queue));
@@ -91,16 +83,16 @@ Kernel::SharedPtr<Kernel::ReadableEvent> NVFlinger::GetVsyncEvent(u64 display_id
}
std::shared_ptr<BufferQueue> NVFlinger::GetBufferQueue(u32 id) const {
- auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
- [&](const auto& queue) { return queue->GetId() == id; });
+ const auto itr = std::find_if(buffer_queues.begin(), buffer_queues.end(),
+ [&](const auto& queue) { return queue->GetId() == id; });
ASSERT(itr != buffer_queues.end());
return *itr;
}
Display& NVFlinger::GetDisplay(u64 display_id) {
- auto itr = std::find_if(displays.begin(), displays.end(),
- [&](const Display& display) { return display.id == display_id; });
+ const auto itr = std::find_if(displays.begin(), displays.end(),
+ [&](const Display& display) { return display.id == display_id; });
ASSERT(itr != displays.end());
return *itr;
@@ -109,8 +101,8 @@ Display& NVFlinger::GetDisplay(u64 display_id) {
Layer& NVFlinger::GetLayer(u64 display_id, u64 layer_id) {
auto& display = GetDisplay(display_id);
- auto itr = std::find_if(display.layers.begin(), display.layers.end(),
- [&](const Layer& layer) { return layer.id == layer_id; });
+ const auto itr = std::find_if(display.layers.begin(), display.layers.end(),
+ [&](const Layer& layer) { return layer.id == layer_id; });
ASSERT(itr != display.layers.end());
return *itr;
@@ -145,7 +137,7 @@ void NVFlinger::Compose() {
continue;
}
- auto& igbp_buffer = buffer->get().igbp_buffer;
+ const 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
@@ -166,7 +158,7 @@ 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::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Pulse,
+ vsync_event = Kernel::WritableEvent::CreateEventPair(kernel, Kernel::ResetType::Sticky,
fmt::format("Display VSync Event {}", id));
}
diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h
index 9abba555b..8f9a0a7f8 100644
--- a/src/core/hle/service/nvflinger/nvflinger.h
+++ b/src/core/hle/service/nvflinger/nvflinger.h
@@ -4,6 +4,7 @@
#pragma once
+#include <array>
#include <memory>
#include <string>
#include <string_view>
@@ -84,7 +85,13 @@ private:
std::shared_ptr<Nvidia::Module> nvdrv;
- std::vector<Display> displays;
+ std::array<Display, 5> displays{{
+ {0, "Default"},
+ {1, "External"},
+ {2, "Edid"},
+ {3, "Internal"},
+ {4, "Null"},
+ }};
std::vector<std::shared_ptr<BufferQueue>> buffer_queues;
/// Id to use for the next layer that is created, this counter is shared among all displays.
diff --git a/src/core/hle/service/pm/pm.cpp b/src/core/hle/service/pm/pm.cpp
index 53e7da9c3..6b27dc4a3 100644
--- a/src/core/hle/service/pm/pm.cpp
+++ b/src/core/hle/service/pm/pm.cpp
@@ -13,7 +13,7 @@ public:
explicit BootMode() : ServiceFramework{"pm:bm"} {
static const FunctionInfo functions[] = {
{0, &BootMode::GetBootMode, "GetBootMode"},
- {1, nullptr, "SetMaintenanceBoot"},
+ {1, &BootMode::SetMaintenanceBoot, "SetMaintenanceBoot"},
};
RegisterHandlers(functions);
}
@@ -24,8 +24,19 @@ private:
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(static_cast<u32>(SystemBootMode::Normal)); // Normal boot mode
+ rb.PushEnum(boot_mode);
}
+
+ void SetMaintenanceBoot(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_PM, "called");
+
+ boot_mode = SystemBootMode::Maintenance;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ SystemBootMode boot_mode = SystemBootMode::Normal;
};
class DebugMonitor final : public ServiceFramework<DebugMonitor> {
diff --git a/src/core/hle/service/pm/pm.h b/src/core/hle/service/pm/pm.h
index 370f2ed72..cc8d3f215 100644
--- a/src/core/hle/service/pm/pm.h
+++ b/src/core/hle/service/pm/pm.h
@@ -9,7 +9,12 @@ class ServiceManager;
}
namespace Service::PM {
-enum class SystemBootMode : u32 { Normal = 0, Maintenance = 1 };
+
+enum class SystemBootMode {
+ Normal,
+ Maintenance,
+};
+
/// Registers all PM services with the specified service manager.
void InstallInterfaces(SM::ServiceManager& service_manager);
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 1ec340466..d25b80ab0 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -70,10 +70,6 @@
#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 {
/**
@@ -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/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index 0d0f63a78..142929124 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -54,13 +54,11 @@ 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) {
@@ -83,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(
@@ -147,12 +145,13 @@ void SM::RegisterService(Kernel::HLERequestContext& ctx) {
const std::string name(name_buf.begin(), end);
- const auto unk_bool = static_cast<bool>(rp.PopRaw<u32>());
- const auto session_count = rp.PopRaw<u32>();
+ const auto is_light = static_cast<bool>(rp.PopRaw<u32>());
+ const auto max_session_count = rp.PopRaw<u32>();
- LOG_DEBUG(Service_SM, "called with unk_bool={}", unk_bool);
+ LOG_DEBUG(Service_SM, "called with name={}, max_session_count={}, is_light={}", name,
+ max_session_count, is_light);
- auto handle = service_manager->RegisterService(name, session_count);
+ 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);
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index 60b201d06..c13640ad8 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -12,9 +12,16 @@
#include "core/hle/kernel/client_session.h"
#include "core/hle/service/time/interface.h"
#include "core/hle/service/time/time.h"
+#include "core/settings.h"
namespace Service::Time {
+static std::chrono::seconds GetSecondsSinceEpoch() {
+ return std::chrono::duration_cast<std::chrono::seconds>(
+ std::chrono::system_clock::now().time_since_epoch()) +
+ Settings::values.custom_rtc_differential;
+}
+
static void PosixToCalendar(u64 posix_time, CalendarTime& calendar_time,
CalendarAdditionalInfo& additional_info,
[[maybe_unused]] const TimeZoneRule& /*rule*/) {
@@ -68,9 +75,7 @@ public:
private:
void GetCurrentTime(Kernel::HLERequestContext& ctx) {
- const s64 time_since_epoch{std::chrono::duration_cast<std::chrono::seconds>(
- std::chrono::system_clock::now().time_since_epoch())
- .count()};
+ const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 4};
@@ -264,14 +269,9 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::RequestParser rp{ctx};
- auto unknown_u8 = rp.PopRaw<u8>();
-
- ClockSnapshot clock_snapshot{};
+ 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()};
- CalendarTime calendar_time{};
+ const s64 time_since_epoch{GetSecondsSinceEpoch().count()};
const std::time_t time(time_since_epoch);
const std::tm* tm = std::localtime(&time);
if (tm == nullptr) {
@@ -280,16 +280,19 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
rb.Push(ResultCode(-1)); // TODO(ogniK): Find appropriate error code
return;
}
- SteadyClockTimePoint steady_clock_time_point{CoreTiming::cyclesToMs(CoreTiming::GetTicks()) /
- 1000};
- LocationName location_name{"UTC"};
+ 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;
@@ -302,9 +305,10 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
clock_snapshot.network_calendar_info = additional_info;
clock_snapshot.steady_clock_timepoint = steady_clock_time_point;
- clock_snapshot.location_name = location_name;
+ clock_snapshot.location_name = LocationName{"UTC"};
clock_snapshot.clock_auto_adjustment_enabled = 1;
- clock_snapshot.ipc_u8 = unknown_u8;
+ clock_snapshot.type = initial_type;
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
ctx.WriteBuffer(&clock_snapshot, sizeof(ClockSnapshot));
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index ea43fbea7..f11affe95 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -22,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");
@@ -30,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,
@@ -42,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");
@@ -66,8 +67,9 @@ struct ClockSnapshot {
SteadyClockTimePoint steady_clock_timepoint;
LocationName location_name;
u8 clock_auto_adjustment_enabled;
- u8 ipc_u8;
- INSERT_PADDING_BYTES(2);
+ u8 type;
+ u8 version;
+ INSERT_PADDING_BYTES(1);
};
static_assert(sizeof(ClockSnapshot) == 0xd0, "ClockSnapshot is an invalid size");
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 311b0c765..0f2c25182 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -19,6 +19,7 @@
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.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"
@@ -31,12 +32,26 @@
namespace Service::VI {
+constexpr ResultCode ERR_OPERATION_FAILED{ErrorModule::VI, 1};
+constexpr ResultCode ERR_UNSUPPORTED{ErrorModule::VI, 6};
+
struct DisplayInfo {
+ /// The name of this particular display.
char display_name[0x40]{"Default"};
- u64 unknown_1{1};
- u64 unknown_2{1};
- u64 width{1280};
- u64 height{720};
+
+ /// Whether or not the display has a limited number of layers.
+ u8 has_limited_layers{1};
+ INSERT_PADDING_BYTES(7){};
+
+ /// Indicates the total amount of layers supported by the display.
+ /// @note This is only valid if has_limited_layers is set.
+ u64 max_layers{1};
+
+ /// Maximum width in pixels.
+ u64 width{1920};
+
+ /// Maximum height in pixels.
+ u64 height{1080};
};
static_assert(sizeof(DisplayInfo) == 0x60, "DisplayInfo has wrong size");
@@ -502,10 +517,12 @@ 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>();
- LOG_DEBUG(Service_VI, "called, transaction={:X}", static_cast<u32>(transaction));
+ 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. id=0x{:08X} transaction={:X}, flags=0x{:08X}", id,
+ static_cast<u32>(transaction), flags);
auto buffer_queue = nv_flinger->GetBufferQueue(id);
@@ -593,9 +610,10 @@ 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);
@@ -605,11 +623,12 @@ private:
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>();
+
LOG_WARNING(Service_VI, "(STUBBED) called id={}, unknown={:08X}", id, unknown);
- auto buffer_queue = nv_flinger->GetBufferQueue(id);
+ const auto buffer_queue = nv_flinger->GetBufferQueue(id);
// TODO(Subv): Find out what this actually is.
IPC::ResponseBuilder rb{ctx, 2, 1};
@@ -674,22 +693,25 @@ 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);
}
+ // This function currently does nothing but return a success error code in
+ // the vi library itself, so do the same thing, but log out the passed in values.
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u64 layer_id = rp.Pop<u64>();
- bool visibility = rp.Pop<bool>();
- LOG_WARNING(Service_VI, "(STUBBED) called, layer_id=0x{:08X}, visibility={}", layer_id,
- visibility);
+ const u64 layer_id = rp.Pop<u64>();
+ const bool visibility = rp.Pop<bool>();
+
+ LOG_DEBUG(Service_VI, "called, layer_id=0x{:08X}, visibility={}", layer_id, visibility);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -796,25 +818,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>();
- u64 layer_id = nv_flinger->CreateLayer(display);
+ LOG_WARNING(Service_VI,
+ "(STUBBED) called. unknown=0x{:08X}, display=0x{:016X}, aruid=0x{:016X}",
+ unknown, display, aruid);
+
+ const u64 layer_id = nv_flinger->CreateLayer(display);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
@@ -822,11 +846,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);
@@ -834,8 +859,9 @@ private:
void SetLayerVisibility(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u64 layer_id = rp.Pop<u64>();
- bool visibility = rp.Pop<bool>();
+ 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);
@@ -852,6 +878,22 @@ public:
~IApplicationDisplayService() = default;
private:
+ enum class ConvertedScaleMode : u64 {
+ Freeze = 0,
+ ScaleToWindow = 1,
+ ScaleAndCrop = 2,
+ None = 3,
+ PreserveAspectRatio = 4,
+ };
+
+ enum class NintendoScaleMode : u32 {
+ None = 0,
+ Freeze = 1,
+ ScaleToWindow = 2,
+ ScaleAndCrop = 3,
+ PreserveAspectRatio = 4,
+ };
+
void GetRelayService(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_VI, "(STUBBED) called");
@@ -888,10 +930,23 @@ private:
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>>();
+
+ OpenDisplayImpl(ctx, std::string_view{name_buf.data(), name_buf.size()});
+ }
+
+ void OpenDefaultDisplay(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_VI, "called");
- std::string name(name_buf.begin(), end);
+ 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");
@@ -901,45 +956,65 @@ 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);
}
- void GetDisplayResolution(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_VI, "(STUBBED) called");
+ // 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) {
IPC::RequestParser rp{ctx};
- u64 display_id = rp.Pop<u64>();
+ const u64 display_id = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_VI, "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) *
- 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) *
- static_cast<u32>(Settings::values.resolution_factor));
- rb.Push(static_cast<u64>(DisplayResolution::UndockedHeight) *
- static_cast<u32>(Settings::values.resolution_factor));
- }
+ // This only returns the fixed values of 1280x720 and makes no distinguishing
+ // between docked and undocked dimensions. We take the liberty of applying
+ // the resolution scaling factor here.
+ 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 auto scaling_mode = rp.PopEnum<NintendoScaleMode>();
+ const u64 unknown = rp.Pop<u64>();
+
+ LOG_DEBUG(Service_VI, "called. scaling_mode=0x{:08X}, unknown=0x{:016X}",
+ static_cast<u32>(scaling_mode), unknown);
IPC::ResponseBuilder rb{ctx, 2};
+
+ if (scaling_mode > NintendoScaleMode::PreserveAspectRatio) {
+ LOG_ERROR(Service_VI, "Invalid scaling mode provided.");
+ rb.Push(ERR_OPERATION_FAILED);
+ return;
+ }
+
+ if (scaling_mode != NintendoScaleMode::ScaleToWindow &&
+ scaling_mode != NintendoScaleMode::PreserveAspectRatio) {
+ LOG_ERROR(Service_VI, "Unsupported scaling mode supplied.");
+ rb.Push(ERR_UNSUPPORTED);
+ return;
+ }
+
rb.Push(RESULT_SUCCESS);
}
@@ -957,19 +1032,19 @@ private:
}
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};
@@ -978,17 +1053,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};
@@ -998,73 +1073,59 @@ 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);
rb.PushCopyObjects(vsync_event);
}
- enum class ConvertedScaleMode : u64 {
- None = 0, // VI seems to name this as "Unknown" but lots of games pass it, assume it's no
- // scaling/default
- Freeze = 1,
- ScaleToWindow = 2,
- Crop = 3,
- NoCrop = 4,
- };
-
- // This struct is different, currently it's 1:1 but this might change in the future.
- enum class NintendoScaleMode : u32 {
- None = 0,
- Freeze = 1,
- ScaleToWindow = 2,
- Crop = 3,
- NoCrop = 4,
- };
-
void ConvertScalingMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- auto mode = rp.PopEnum<NintendoScaleMode>();
+ const auto mode = rp.PopEnum<NintendoScaleMode>();
LOG_DEBUG(Service_VI, "called mode={}", static_cast<u32>(mode));
- IPC::ResponseBuilder rb{ctx, 4};
- rb.Push(RESULT_SUCCESS);
+ const auto converted_mode = ConvertScalingModeImpl(mode);
+
+ if (converted_mode.Succeeded()) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(*converted_mode);
+ } else {
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(converted_mode.Code());
+ }
+ }
+
+ static ResultVal<ConvertedScaleMode> ConvertScalingModeImpl(NintendoScaleMode mode) {
switch (mode) {
case NintendoScaleMode::None:
- rb.PushEnum(ConvertedScaleMode::None);
- break;
+ return MakeResult(ConvertedScaleMode::None);
case NintendoScaleMode::Freeze:
- rb.PushEnum(ConvertedScaleMode::Freeze);
- break;
+ return MakeResult(ConvertedScaleMode::Freeze);
case NintendoScaleMode::ScaleToWindow:
- rb.PushEnum(ConvertedScaleMode::ScaleToWindow);
- break;
- case NintendoScaleMode::Crop:
- rb.PushEnum(ConvertedScaleMode::Crop);
- break;
- case NintendoScaleMode::NoCrop:
- rb.PushEnum(ConvertedScaleMode::NoCrop);
- break;
+ return MakeResult(ConvertedScaleMode::ScaleToWindow);
+ case NintendoScaleMode::ScaleAndCrop:
+ return MakeResult(ConvertedScaleMode::ScaleAndCrop);
+ case NintendoScaleMode::PreserveAspectRatio:
+ return MakeResult(ConvertedScaleMode::PreserveAspectRatio);
default:
- UNIMPLEMENTED_MSG("Unknown scaling mode {}", static_cast<u32>(mode));
- rb.PushEnum(ConvertedScaleMode::None);
- break;
+ return ERR_OPERATION_FAILED;
}
}
@@ -1082,9 +1143,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"},