summaryrefslogtreecommitdiffstats
path: root/src/core
diff options
context:
space:
mode:
Diffstat (limited to 'src/core')
-rw-r--r--src/core/CMakeLists.txt4
-rw-r--r--src/core/hid/hid_core.cpp10
-rw-r--r--src/core/hid/hid_core.h3
-rw-r--r--src/core/hle/kernel/init/init_slab_setup.cpp2
-rw-r--r--src/core/hle/kernel/k_class_token.cpp5
-rw-r--r--src/core/hle/kernel/k_code_memory.cpp146
-rw-r--r--src/core/hle/kernel/k_code_memory.h66
-rw-r--r--src/core/hle/kernel/k_memory_block.h20
-rw-r--r--src/core/hle/kernel/k_page_linked_list.h4
-rw-r--r--src/core/hle/kernel/k_page_table.cpp119
-rw-r--r--src/core/hle/kernel/k_page_table.h8
-rw-r--r--src/core/hle/kernel/kernel.h4
-rw-r--r--src/core/hle/kernel/svc.cpp214
-rw-r--r--src/core/hle/kernel/svc_wrap.h27
-rw-r--r--src/core/hle/service/glue/glue.cpp4
-rw-r--r--src/core/hle/service/glue/notif.cpp44
-rw-r--r--src/core/hle/service/glue/notif.h25
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp165
-rw-r--r--src/core/hle/service/hid/controllers/npad.h8
-rw-r--r--src/core/hle/service/hid/hid.cpp26
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp13
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp5
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h3
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.cpp10
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h3
26 files changed, 889 insertions, 52 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index cd99b447b..49bed614a 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -179,6 +179,8 @@ add_library(core STATIC
hle/kernel/k_client_port.h
hle/kernel/k_client_session.cpp
hle/kernel/k_client_session.h
+ hle/kernel/k_code_memory.cpp
+ hle/kernel/k_code_memory.h
hle/kernel/k_condition_variable.cpp
hle/kernel/k_condition_variable.h
hle/kernel/k_event.cpp
@@ -410,6 +412,8 @@ add_library(core STATIC
hle/service/glue/glue.h
hle/service/glue/glue_manager.cpp
hle/service/glue/glue_manager.h
+ hle/service/glue/notif.cpp
+ hle/service/glue/notif.h
hle/service/grc/grc.cpp
hle/service/grc/grc.h
hle/service/hid/hid.cpp
diff --git a/src/core/hid/hid_core.cpp b/src/core/hid/hid_core.cpp
index 0c3eb5a62..a1c3bbb57 100644
--- a/src/core/hid/hid_core.cpp
+++ b/src/core/hid/hid_core.cpp
@@ -145,6 +145,16 @@ NpadIdType HIDCore::GetFirstNpadId() const {
return NpadIdType::Player1;
}
+NpadIdType HIDCore::GetFirstDisconnectedNpadId() const {
+ for (std::size_t player_index = 0; player_index < available_controllers; ++player_index) {
+ const auto* const controller = GetEmulatedControllerByIndex(player_index);
+ if (!controller->IsConnected()) {
+ return controller->GetNpadIdType();
+ }
+ }
+ return NpadIdType::Player1;
+}
+
void HIDCore::EnableAllControllerConfiguration() {
player_1->EnableConfiguration();
player_2->EnableConfiguration();
diff --git a/src/core/hid/hid_core.h b/src/core/hid/hid_core.h
index 2fb0f7e19..837f7de49 100644
--- a/src/core/hid/hid_core.h
+++ b/src/core/hid/hid_core.h
@@ -45,6 +45,9 @@ public:
/// Returns the first connected npad id
NpadIdType GetFirstNpadId() const;
+ /// Returns the first disconnected npad id
+ NpadIdType GetFirstDisconnectedNpadId() const;
+
/// Sets all emulated controllers into configuring mode.
void EnableAllControllerConfiguration();
diff --git a/src/core/hle/kernel/init/init_slab_setup.cpp b/src/core/hle/kernel/init/init_slab_setup.cpp
index 8ff0f695d..36fc0944a 100644
--- a/src/core/hle/kernel/init/init_slab_setup.cpp
+++ b/src/core/hle/kernel/init/init_slab_setup.cpp
@@ -9,6 +9,7 @@
#include "core/core.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/init/init_slab_setup.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_memory_layout.h"
#include "core/hle/kernel/k_memory_manager.h"
@@ -32,6 +33,7 @@ namespace Kernel::Init {
HANDLER(KPort, (SLAB_COUNT(KPort)), ##__VA_ARGS__) \
HANDLER(KSharedMemory, (SLAB_COUNT(KSharedMemory)), ##__VA_ARGS__) \
HANDLER(KTransferMemory, (SLAB_COUNT(KTransferMemory)), ##__VA_ARGS__) \
+ HANDLER(KCodeMemory, (SLAB_COUNT(KCodeMemory)), ##__VA_ARGS__) \
HANDLER(KSession, (SLAB_COUNT(KSession)), ##__VA_ARGS__) \
HANDLER(KResourceLimit, (SLAB_COUNT(KResourceLimit)), ##__VA_ARGS__)
diff --git a/src/core/hle/kernel/k_class_token.cpp b/src/core/hle/kernel/k_class_token.cpp
index 0be0027be..21e2fe494 100644
--- a/src/core/hle/kernel/k_class_token.cpp
+++ b/src/core/hle/kernel/k_class_token.cpp
@@ -6,6 +6,7 @@
#include "core/hle/kernel/k_class_token.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_port.h"
#include "core/hle/kernel/k_process.h"
@@ -48,7 +49,7 @@ static_assert(ClassToken<KWritableEvent> == 0b10001001'00000000);
static_assert(ClassToken<KTransferMemory> == 0b10010001'00000000);
// static_assert(ClassToken<KDeviceAddressSpace> == 0b01100001'00000000);
// static_assert(ClassToken<KSessionRequest> == 0b10100001'00000000);
-// static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
+static_assert(ClassToken<KCodeMemory> == 0b11000001'00000000);
// Ensure that the token hierarchy is correct.
@@ -79,7 +80,7 @@ static_assert(ClassToken<KWritableEvent> == ((0b10001001 << 8) | ClassToken<KAut
static_assert(ClassToken<KTransferMemory> == ((0b10010001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KDeviceAddressSpace> == ((0b01100001 << 8) | ClassToken<KAutoObject>));
// static_assert(ClassToken<KSessionRequest> == ((0b10100001 << 8) | ClassToken<KAutoObject>));
-// static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
+static_assert(ClassToken<KCodeMemory> == ((0b11000001 << 8) | ClassToken<KAutoObject>));
// Ensure that the token hierarchy reflects the class hierarchy.
diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp
new file mode 100644
index 000000000..d69f7ffb7
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.cpp
@@ -0,0 +1,146 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_code_memory.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_memory_block.h"
+#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_page_table.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+KCodeMemory::KCodeMemory(KernelCore& kernel_)
+ : KAutoObjectWithSlabHeapAndContainer{kernel_}, m_lock(kernel_) {}
+
+ResultCode KCodeMemory::Initialize(Core::DeviceMemory& device_memory, VAddr addr, size_t size) {
+ // Set members.
+ m_owner = kernel.CurrentProcess();
+
+ // Get the owner page table.
+ auto& page_table = m_owner->PageTable();
+
+ // Construct the page group.
+ KMemoryInfo kBlockInfo = page_table.QueryInfo(addr);
+ m_page_group = KPageLinkedList(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+
+ // Lock the memory.
+ R_TRY(page_table.LockForCodeMemory(addr, size))
+
+ // Clear the memory.
+ for (const auto& block : m_page_group.Nodes()) {
+ std::memset(device_memory.GetPointer(block.GetAddress()), 0xFF, block.GetSize());
+ }
+
+ // Set remaining tracking members.
+ m_address = addr;
+ m_is_initialized = true;
+ m_is_owner_mapped = false;
+ m_is_mapped = false;
+
+ // We succeeded.
+ return ResultSuccess;
+}
+
+void KCodeMemory::Finalize() {
+ // Unlock.
+ if (!m_is_mapped && !m_is_owner_mapped) {
+ const size_t size = m_page_group.GetNumPages() * PageSize;
+ m_owner->PageTable().UnlockForCodeMemory(m_address, size);
+ }
+}
+
+ResultCode KCodeMemory::Map(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_mapped, ResultInvalidState);
+
+ // Map the memory.
+ R_TRY(kernel.CurrentProcess()->PageTable().MapPages(
+ address, m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));
+
+ // Mark ourselves as mapped.
+ m_is_mapped = true;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::Unmap(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, m_page_group,
+ KMemoryState::CodeOut));
+
+ // Mark ourselves as unmapped.
+ m_is_mapped = false;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Ensure we're not already mapped.
+ R_UNLESS(!m_is_owner_mapped, ResultInvalidState);
+
+ // Convert the memory permission.
+ KMemoryPermission k_perm{};
+ switch (perm) {
+ case Svc::MemoryPermission::Read:
+ k_perm = KMemoryPermission::UserRead;
+ break;
+ case Svc::MemoryPermission::ReadExecute:
+ k_perm = KMemoryPermission::UserReadExecute;
+ break;
+ default:
+ break;
+ }
+
+ // Map the memory.
+ R_TRY(
+ m_owner->PageTable().MapPages(address, m_page_group, KMemoryState::GeneratedCode, k_perm));
+
+ // Mark ourselves as mapped.
+ m_is_owner_mapped = true;
+
+ return ResultSuccess;
+}
+
+ResultCode KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {
+ // Validate the size.
+ R_UNLESS(m_page_group.GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize);
+
+ // Lock ourselves.
+ KScopedLightLock lk(m_lock);
+
+ // Unmap the memory.
+ R_TRY(m_owner->PageTable().UnmapPages(address, m_page_group, KMemoryState::GeneratedCode));
+
+ // Mark ourselves as unmapped.
+ m_is_owner_mapped = false;
+
+ return ResultSuccess;
+}
+
+} // namespace Kernel \ No newline at end of file
diff --git a/src/core/hle/kernel/k_code_memory.h b/src/core/hle/kernel/k_code_memory.h
new file mode 100644
index 000000000..e0ba19a53
--- /dev/null
+++ b/src/core/hle/kernel/k_code_memory.h
@@ -0,0 +1,66 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_types.h"
+#include "core/device_memory.h"
+#include "core/hle/kernel/k_auto_object.h"
+#include "core/hle/kernel/k_light_lock.h"
+#include "core/hle/kernel/k_page_linked_list.h"
+#include "core/hle/kernel/k_process.h"
+#include "core/hle/kernel/slab_helpers.h"
+#include "core/hle/kernel/svc_types.h"
+#include "core/hle/result.h"
+
+namespace Kernel {
+
+enum class CodeMemoryOperation : u32 {
+ Map = 0,
+ MapToOwner = 1,
+ Unmap = 2,
+ UnmapFromOwner = 3,
+};
+
+class KCodeMemory final
+ : public KAutoObjectWithSlabHeapAndContainer<KCodeMemory, KAutoObjectWithList> {
+ KERNEL_AUTOOBJECT_TRAITS(KCodeMemory, KAutoObject);
+
+public:
+ explicit KCodeMemory(KernelCore& kernel_);
+
+ ResultCode Initialize(Core::DeviceMemory& device_memory, VAddr address, size_t size);
+ void Finalize();
+
+ ResultCode Map(VAddr address, size_t size);
+ ResultCode Unmap(VAddr address, size_t size);
+ ResultCode MapToOwner(VAddr address, size_t size, Svc::MemoryPermission perm);
+ ResultCode UnmapFromOwner(VAddr address, size_t size);
+
+ bool IsInitialized() const {
+ return m_is_initialized;
+ }
+ static void PostDestroy([[maybe_unused]] uintptr_t arg) {}
+
+ KProcess* GetOwner() const {
+ return m_owner;
+ }
+ VAddr GetSourceAddress() const {
+ return m_address;
+ }
+ size_t GetSize() const {
+ return m_is_initialized ? m_page_group.GetNumPages() * PageSize : 0;
+ }
+
+private:
+ KPageLinkedList m_page_group{};
+ KProcess* m_owner{};
+ VAddr m_address{};
+ KLightLock m_lock;
+ bool m_is_initialized{};
+ bool m_is_owner_mapped{};
+ bool m_is_mapped{};
+};
+
+} // namespace Kernel
diff --git a/src/core/hle/kernel/k_memory_block.h b/src/core/hle/kernel/k_memory_block.h
index a7fdb5fb8..fd491146f 100644
--- a/src/core/hle/kernel/k_memory_block.h
+++ b/src/core/hle/kernel/k_memory_block.h
@@ -131,6 +131,26 @@ enum class KMemoryPermission : u8 {
UserMask = static_cast<u8>(Svc::MemoryPermission::Read | Svc::MemoryPermission::Write |
Svc::MemoryPermission::Execute),
+
+ KernelShift = 3,
+
+ KernelRead = Read << KernelShift,
+ KernelWrite = Write << KernelShift,
+ KernelExecute = Execute << KernelShift,
+
+ NotMapped = (1 << (2 * KernelShift)),
+
+ KernelReadWrite = KernelRead | KernelWrite,
+ KernelReadExecute = KernelRead | KernelExecute,
+
+ UserRead = Read | KernelRead,
+ UserWrite = Write | KernelWrite,
+ UserExecute = Execute,
+
+ UserReadWrite = UserRead | UserWrite,
+ UserReadExecute = UserRead | UserExecute,
+
+ IpcLockChangeMask = NotMapped | UserReadWrite
};
DECLARE_ENUM_FLAG_OPERATORS(KMemoryPermission);
diff --git a/src/core/hle/kernel/k_page_linked_list.h b/src/core/hle/kernel/k_page_linked_list.h
index 3362fb236..0e2ae582a 100644
--- a/src/core/hle/kernel/k_page_linked_list.h
+++ b/src/core/hle/kernel/k_page_linked_list.h
@@ -27,6 +27,10 @@ public:
return num_pages;
}
+ constexpr std::size_t GetSize() const {
+ return GetNumPages() * PageSize;
+ }
+
private:
u64 addr{};
std::size_t num_pages{};
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp
index 9bda5c5b2..99982e5a3 100644
--- a/src/core/hle/kernel/k_page_table.cpp
+++ b/src/core/hle/kernel/k_page_table.cpp
@@ -368,6 +368,33 @@ ResultCode KPageTable::UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, st
return ResultSuccess;
}
+ResultCode KPageTable::UnmapProcessMemory(VAddr dst_addr, std::size_t size,
+ KPageTable& src_page_table, VAddr src_addr) {
+ std::lock_guard lock{page_table_lock};
+
+ const std::size_t num_pages{size / PageSize};
+
+ // Check that the memory is mapped in the destination process.
+ size_t num_allocator_blocks;
+ R_TRY(CheckMemoryState(&num_allocator_blocks, dst_addr, size, KMemoryState::All,
+ KMemoryState::SharedCode, KMemoryPermission::UserReadWrite,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ // Check that the memory is mapped in the source process.
+ R_TRY(src_page_table.CheckMemoryState(src_addr, size, KMemoryState::FlagCanMapProcess,
+ KMemoryState::FlagCanMapProcess, KMemoryPermission::None,
+ KMemoryPermission::None, KMemoryAttribute::All,
+ KMemoryAttribute::None));
+
+ CASCADE_CODE(Operate(dst_addr, num_pages, KMemoryPermission::None, OperationType::Unmap));
+
+ // Apply the memory block update.
+ block_manager->Update(dst_addr, num_pages, KMemoryState::Free, KMemoryPermission::None,
+ KMemoryAttribute::None);
+
+ return ResultSuccess;
+}
void KPageTable::MapPhysicalMemory(KPageLinkedList& page_linked_list, VAddr start, VAddr end) {
auto node{page_linked_list.Nodes().begin()};
PAddr map_addr{node->GetAddress()};
@@ -942,6 +969,60 @@ ResultCode KPageTable::UnlockForDeviceAddressSpace(VAddr addr, std::size_t size)
return ResultSuccess;
}
+ResultCode KPageTable::LockForCodeMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ KMemoryPermission new_perm = KMemoryPermission::NotMapped | KMemoryPermission::KernelReadWrite;
+
+ KMemoryPermission old_perm{};
+
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::Mask,
+ KMemoryPermission::UserReadWrite, KMemoryAttribute::All, KMemoryAttribute::None)};
+ result.IsError()) {
+ return result;
+ }
+
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
+ block->ShareToDevice(permission);
+ },
+ new_perm);
+
+ return ResultSuccess;
+}
+
+ResultCode KPageTable::UnlockForCodeMemory(VAddr addr, std::size_t size) {
+ std::lock_guard lock{page_table_lock};
+
+ KMemoryPermission new_perm = KMemoryPermission::UserReadWrite;
+
+ KMemoryPermission old_perm{};
+
+ if (const ResultCode result{CheckMemoryState(
+ nullptr, &old_perm, nullptr, addr, size, KMemoryState::FlagCanCodeMemory,
+ KMemoryState::FlagCanCodeMemory, KMemoryPermission::None, KMemoryPermission::None,
+ KMemoryAttribute::All, KMemoryAttribute::Locked)};
+ result.IsError()) {
+ return result;
+ }
+
+ new_perm = (new_perm != KMemoryPermission::None) ? new_perm : old_perm;
+
+ block_manager->UpdateLock(
+ addr, size / PageSize,
+ [](KMemoryBlockManager::iterator block, KMemoryPermission permission) {
+ block->UnshareToDevice(permission);
+ },
+ new_perm);
+
+ return ResultSuccess;
+}
+
ResultCode KPageTable::InitializeMemoryLayout(VAddr start, VAddr end) {
block_manager = std::make_unique<KMemoryBlockManager>(start, end);
@@ -1231,4 +1312,42 @@ ResultCode KPageTable::CheckMemoryState(KMemoryState* out_state, KMemoryPermissi
return ResultSuccess;
}
+ResultCode KPageTable::CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const {
+ // Get information about the first block.
+ const VAddr last_addr = addr + size - 1;
+ KMemoryBlockManager::const_iterator it{block_manager->FindIterator(addr)};
+ KMemoryInfo info = it->GetMemoryInfo();
+
+ // If the start address isn't aligned, we need a block.
+ const size_t blocks_for_start_align =
+ (Common::AlignDown(addr, PageSize) != info.GetAddress()) ? 1 : 0;
+
+ while (true) {
+ // Validate against the provided masks.
+ R_TRY(CheckMemoryState(info, state_mask, state, perm_mask, perm, attr_mask, attr));
+
+ // Break once we're done.
+ if (last_addr <= info.GetLastAddress()) {
+ break;
+ }
+
+ // Advance our iterator.
+ it++;
+ info = it->GetMemoryInfo();
+ }
+
+ // If the end address isn't aligned, we need a block.
+ const size_t blocks_for_end_align =
+ (Common::AlignUp(addr + size, PageSize) != info.GetEndAddress()) ? 1 : 0;
+
+ if (out_blocks_needed != nullptr) {
+ *out_blocks_needed = blocks_for_start_align + blocks_for_end_align;
+ }
+
+ return ResultSuccess;
+}
+
} // namespace Kernel
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h
index b7ec38f06..d784aa67e 100644
--- a/src/core/hle/kernel/k_page_table.h
+++ b/src/core/hle/kernel/k_page_table.h
@@ -33,6 +33,8 @@ public:
KMemoryPermission perm);
ResultCode MapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
ResultCode UnmapProcessCodeMemory(VAddr dst_addr, VAddr src_addr, std::size_t size);
+ ResultCode UnmapProcessMemory(VAddr dst_addr, std::size_t size, KPageTable& src_page_table,
+ VAddr src_addr);
ResultCode MapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapPhysicalMemory(VAddr addr, std::size_t size);
ResultCode UnmapMemory(VAddr addr, std::size_t size);
@@ -55,6 +57,8 @@ public:
KMemoryPermission perm, PAddr map_addr = 0);
ResultCode LockForDeviceAddressSpace(VAddr addr, std::size_t size);
ResultCode UnlockForDeviceAddressSpace(VAddr addr, std::size_t size);
+ ResultCode LockForCodeMemory(VAddr addr, std::size_t size);
+ ResultCode UnlockForCodeMemory(VAddr addr, std::size_t size);
Common::PageTable& PageTableImpl() {
return page_table_impl;
@@ -115,6 +119,10 @@ private:
return CheckMemoryState(nullptr, nullptr, nullptr, addr, size, state_mask, state, perm_mask,
perm, attr_mask, attr, ignore_attr);
}
+ ResultCode CheckMemoryState(size_t* out_blocks_needed, VAddr addr, size_t size,
+ KMemoryState state_mask, KMemoryState state,
+ KMemoryPermission perm_mask, KMemoryPermission perm,
+ KMemoryAttribute attr_mask, KMemoryAttribute attr) const;
std::recursive_mutex page_table_lock;
std::unique_ptr<KMemoryBlockManager> block_manager;
diff --git a/src/core/hle/kernel/kernel.h b/src/core/hle/kernel/kernel.h
index eacf9dc61..b9b423908 100644
--- a/src/core/hle/kernel/kernel.h
+++ b/src/core/hle/kernel/kernel.h
@@ -53,6 +53,7 @@ class KSharedMemoryInfo;
class KThread;
class KTransferMemory;
class KWritableEvent;
+class KCodeMemory;
class PhysicalCore;
class ServiceThread;
class Synchronization;
@@ -331,6 +332,8 @@ public:
return slab_heap_container->transfer_memory;
} else if constexpr (std::is_same_v<T, KWritableEvent>) {
return slab_heap_container->writeable_event;
+ } else if constexpr (std::is_same_v<T, KCodeMemory>) {
+ return slab_heap_container->code_memory;
}
}
@@ -382,6 +385,7 @@ private:
KSlabHeap<KThread> thread;
KSlabHeap<KTransferMemory> transfer_memory;
KSlabHeap<KWritableEvent> writeable_event;
+ KSlabHeap<KCodeMemory> code_memory;
};
std::unique_ptr<SlabHeapContainer> slab_heap_container;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 359cf515d..a9f7438ea 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -18,6 +18,7 @@
#include "core/core_timing.h"
#include "core/hle/kernel/k_client_port.h"
#include "core/hle/kernel/k_client_session.h"
+#include "core/hle/kernel/k_code_memory.h"
#include "core/hle/kernel/k_event.h"
#include "core/hle/kernel/k_handle_table.h"
#include "core/hle/kernel/k_memory_block.h"
@@ -1202,6 +1203,22 @@ constexpr bool IsValidProcessMemoryPermission(Svc::MemoryPermission perm) {
}
}
+constexpr bool IsValidMapCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::ReadWrite;
+}
+
+constexpr bool IsValidMapToOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::Read || perm == Svc::MemoryPermission::ReadExecute;
+}
+
+constexpr bool IsValidUnmapCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::None;
+}
+
+constexpr bool IsValidUnmapFromOwnerCodeMemoryPermission(Svc::MemoryPermission perm) {
+ return perm == Svc::MemoryPermission::None;
+}
+
} // Anonymous namespace
static ResultCode MapSharedMemory(Core::System& system, Handle shmem_handle, VAddr address,
@@ -1311,6 +1328,195 @@ static ResultCode SetProcessMemoryPermission(Core::System& system, Handle proces
return page_table.SetProcessMemoryPermission(address, size, ConvertToKMemoryPermission(perm));
}
+static ResultCode MapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = system.CurrentProcess();
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Create a new page group.
+ KMemoryInfo kBlockInfo = dst_pt.QueryInfo(dst_address);
+ KPageLinkedList pg(kBlockInfo.GetAddress(), kBlockInfo.GetNumPages());
+
+ // Map the group.
+ R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode,
+ KMemoryPermission::UserReadWrite));
+
+ return ResultSuccess;
+}
+
+static ResultCode UnmapProcessMemory(Core::System& system, VAddr dst_address, Handle process_handle,
+ VAddr src_address, u64 size) {
+ LOG_TRACE(Kernel_SVC,
+ "called, dst_address=0x{:X}, process_handle=0x{:X}, src_address=0x{:X}, size=0x{:X}",
+ dst_address, process_handle, src_address, size);
+
+ // Validate the address/size.
+ R_UNLESS(Common::IsAligned(dst_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(src_address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((dst_address < dst_address + size), ResultInvalidCurrentMemory);
+ R_UNLESS((src_address < src_address + size), ResultInvalidCurrentMemory);
+
+ // Get the processes.
+ KProcess* dst_process = system.CurrentProcess();
+ KScopedAutoObject src_process =
+ dst_process->GetHandleTable().GetObjectWithoutPseudoHandle<KProcess>(process_handle);
+ R_UNLESS(src_process.IsNotNull(), ResultInvalidHandle);
+
+ // Get the page tables.
+ auto& dst_pt = dst_process->PageTable();
+ auto& src_pt = src_process->PageTable();
+
+ // Validate that the mapping is in range.
+ R_UNLESS(src_pt.Contains(src_address, size), ResultInvalidCurrentMemory);
+ R_UNLESS(dst_pt.CanContain(dst_address, size, KMemoryState::SharedCode),
+ ResultInvalidMemoryRegion);
+
+ // Unmap the memory.
+ R_TRY(dst_pt.UnmapProcessMemory(dst_address, size, src_pt, src_address));
+
+ return ResultSuccess;
+}
+
+static ResultCode CreateCodeMemory(Core::System& system, Handle* out, VAddr address, size_t size) {
+ LOG_TRACE(Kernel_SVC, "called, handle_out=0x{:X}, address=0x{:X}, size=0x{:X}",
+ static_cast<void*>(out), address, size);
+ // Get kernel instance.
+ auto& kernel = system.Kernel();
+
+ // Validate address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Create the code memory.
+
+ KCodeMemory* code_mem = KCodeMemory::Create(kernel);
+ R_UNLESS(code_mem != nullptr, ResultOutOfResource);
+
+ // Verify that the region is in range.
+ R_UNLESS(system.CurrentProcess()->PageTable().Contains(address, size),
+ ResultInvalidCurrentMemory);
+
+ // Initialize the code memory.
+ R_TRY(code_mem->Initialize(system.DeviceMemory(), address, size));
+
+ // Register the code memory.
+ KCodeMemory::Register(kernel, code_mem);
+
+ // Add the code memory to the handle table.
+ R_TRY(system.CurrentProcess()->GetHandleTable().Add(out, code_mem));
+
+ code_mem->Close();
+
+ return ResultSuccess;
+}
+
+static ResultCode ControlCodeMemory(Core::System& system, Handle code_memory_handle, u32 operation,
+ VAddr address, size_t size, Svc::MemoryPermission perm) {
+
+ LOG_TRACE(Kernel_SVC,
+ "called, code_memory_handle=0x{:X}, operation=0x{:X}, address=0x{:X}, size=0x{:X}, "
+ "permission=0x{:X}",
+ code_memory_handle, operation, address, size, perm);
+
+ // Validate the address / size.
+ R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress);
+ R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize);
+ R_UNLESS(size > 0, ResultInvalidSize);
+ R_UNLESS((address < address + size), ResultInvalidCurrentMemory);
+
+ // Get the code memory from its handle.
+ KScopedAutoObject code_mem =
+ system.CurrentProcess()->GetHandleTable().GetObject<KCodeMemory>(code_memory_handle);
+ R_UNLESS(code_mem.IsNotNull(), ResultInvalidHandle);
+
+ // NOTE: Here, Atmosphere extends the SVC to allow code memory operations on one's own process.
+ // This enables homebrew usage of these SVCs for JIT.
+
+ // Perform the operation.
+ switch (static_cast<CodeMemoryOperation>(operation)) {
+ case CodeMemoryOperation::Map: {
+ // Check that the region is in range.
+ R_UNLESS(
+ system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory.
+ R_TRY(code_mem->Map(address, size));
+ } break;
+ case CodeMemoryOperation::Unmap: {
+ // Check that the region is in range.
+ R_UNLESS(
+ system.CurrentProcess()->PageTable().CanContain(address, size, KMemoryState::CodeOut),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory.
+ R_TRY(code_mem->Unmap(address, size));
+ } break;
+ case CodeMemoryOperation::MapToOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidMapToOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Map the memory to its owner.
+ R_TRY(code_mem->MapToOwner(address, size, perm));
+ } break;
+ case CodeMemoryOperation::UnmapFromOwner: {
+ // Check that the region is in range.
+ R_UNLESS(code_mem->GetOwner()->PageTable().CanContain(address, size,
+ KMemoryState::GeneratedCode),
+ ResultInvalidMemoryRegion);
+
+ // Check the memory permission.
+ R_UNLESS(IsValidUnmapFromOwnerCodeMemoryPermission(perm), ResultInvalidNewMemoryPermission);
+
+ // Unmap the memory from its owner.
+ R_TRY(code_mem->UnmapFromOwner(address, size));
+ } break;
+ default:
+ return ResultInvalidEnumValue;
+ }
+
+ return ResultSuccess;
+}
+
static ResultCode QueryProcessMemory(Core::System& system, VAddr memory_info_address,
VAddr page_info_address, Handle process_handle,
VAddr address) {
@@ -2605,8 +2811,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x48, nullptr, "MapPhysicalMemoryUnsafe"},
{0x49, nullptr, "UnmapPhysicalMemoryUnsafe"},
{0x4A, nullptr, "SetUnsafeLimit"},
- {0x4B, nullptr, "CreateCodeMemory"},
- {0x4C, nullptr, "ControlCodeMemory"},
+ {0x4B, SvcWrap64<CreateCodeMemory>, "CreateCodeMemory"},
+ {0x4C, SvcWrap64<ControlCodeMemory>, "ControlCodeMemory"},
{0x4D, nullptr, "SleepSystem"},
{0x4E, nullptr, "ReadWriteRegister"},
{0x4F, nullptr, "SetProcessActivity"},
@@ -2646,8 +2852,8 @@ static const FunctionDef SVC_Table_64[] = {
{0x71, nullptr, "ManageNamedPort"},
{0x72, nullptr, "ConnectToPort"},
{0x73, SvcWrap64<SetProcessMemoryPermission>, "SetProcessMemoryPermission"},
- {0x74, nullptr, "MapProcessMemory"},
- {0x75, nullptr, "UnmapProcessMemory"},
+ {0x74, SvcWrap64<MapProcessMemory>, "MapProcessMemory"},
+ {0x75, SvcWrap64<UnmapProcessMemory>, "UnmapProcessMemory"},
{0x76, SvcWrap64<QueryProcessMemory>, "QueryProcessMemory"},
{0x77, SvcWrap64<MapProcessCodeMemory>, "MapProcessCodeMemory"},
{0x78, SvcWrap64<UnmapProcessCodeMemory>, "UnmapProcessCodeMemory"},
diff --git a/src/core/hle/kernel/svc_wrap.h b/src/core/hle/kernel/svc_wrap.h
index 6e62e656f..86255fe6d 100644
--- a/src/core/hle/kernel/svc_wrap.h
+++ b/src/core/hle/kernel/svc_wrap.h
@@ -73,6 +73,23 @@ void SvcWrap64(Core::System& system) {
.raw);
}
+// Used by MapProcessMemory and UnmapProcessMemory
+template <ResultCode func(Core::System&, u64, u32, u64, u64)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, Param(system, 0), static_cast<u32>(Param(system, 1)),
+ Param(system, 2), Param(system, 3))
+ .raw);
+}
+
+// Used by ControlCodeMemory
+template <ResultCode func(Core::System&, Handle, u32, u64, u64, Svc::MemoryPermission)>
+void SvcWrap64(Core::System& system) {
+ FuncReturn(system, func(system, static_cast<Handle>(Param(system, 0)),
+ static_cast<u32>(Param(system, 1)), Param(system, 2), Param(system, 3),
+ static_cast<Svc::MemoryPermission>(Param(system, 4)))
+ .raw);
+}
+
template <ResultCode func(Core::System&, u32*)>
void SvcWrap64(Core::System& system) {
u32 param = 0;
@@ -301,6 +318,16 @@ void SvcWrap64(Core::System& system) {
FuncReturn(system, retval);
}
+// Used by CreateCodeMemory
+template <ResultCode func(Core::System&, Handle*, u64, u64)>
+void SvcWrap64(Core::System& system) {
+ u32 param_1 = 0;
+ const u32 retval = func(system, &param_1, Param(system, 1), Param(system, 2)).raw;
+
+ system.CurrentArmInterface().SetReg(1, param_1);
+ FuncReturn(system, retval);
+}
+
template <ResultCode func(Core::System&, Handle*, u64, u32, u32)>
void SvcWrap64(Core::System& system) {
u32 param_1 = 0;
diff --git a/src/core/hle/service/glue/glue.cpp b/src/core/hle/service/glue/glue.cpp
index a08dc9758..b24d469cf 100644
--- a/src/core/hle/service/glue/glue.cpp
+++ b/src/core/hle/service/glue/glue.cpp
@@ -8,6 +8,7 @@
#include "core/hle/service/glue/bgtc.h"
#include "core/hle/service/glue/ectx.h"
#include "core/hle/service/glue/glue.h"
+#include "core/hle/service/glue/notif.h"
namespace Service::Glue {
@@ -24,6 +25,9 @@ void InstallInterfaces(Core::System& system) {
// Error Context
std::make_shared<ECTX_AW>(system)->InstallAsService(system.ServiceManager());
+
+ // Notification Services for application
+ std::make_shared<NOTIF_A>(system)->InstallAsService(system.ServiceManager());
}
} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.cpp b/src/core/hle/service/glue/notif.cpp
new file mode 100644
index 000000000..c559ec9df
--- /dev/null
+++ b/src/core/hle/service/glue/notif.cpp
@@ -0,0 +1,44 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/service/glue/notif.h"
+
+namespace Service::Glue {
+
+NOTIF_A::NOTIF_A(Core::System& system_) : ServiceFramework{system_, "notif:a"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {500, nullptr, "RegisterAlarmSetting"},
+ {510, nullptr, "UpdateAlarmSetting"},
+ {520, &NOTIF_A::ListAlarmSettings, "ListAlarmSettings"},
+ {530, nullptr, "LoadApplicationParameter"},
+ {540, nullptr, "DeleteAlarmSetting"},
+ {1000, &NOTIF_A::Initialize, "Initialize"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+}
+
+NOTIF_A::~NOTIF_A() = default;
+
+void NOTIF_A::ListAlarmSettings(Kernel::HLERequestContext& ctx) {
+ // Returns an array of AlarmSetting
+ constexpr s32 alarm_count = 0;
+
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(ResultSuccess);
+ rb.Push(alarm_count);
+}
+
+void NOTIF_A::Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NOTIF, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(ResultSuccess);
+}
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/glue/notif.h b/src/core/hle/service/glue/notif.h
new file mode 100644
index 000000000..6ecf2015c
--- /dev/null
+++ b/src/core/hle/service/glue/notif.h
@@ -0,0 +1,25 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/hle/service/service.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::Glue {
+
+class NOTIF_A final : public ServiceFramework<NOTIF_A> {
+public:
+ explicit NOTIF_A(Core::System& system_);
+ ~NOTIF_A() override;
+
+private:
+ void ListAlarmSettings(Kernel::HLERequestContext& ctx);
+ void Initialize(Kernel::HLERequestContext& ctx);
+};
+
+} // namespace Service::Glue
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index ae56f10cf..2705e9dcb 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -110,7 +110,7 @@ void Controller_NPad::ControllerUpdate(Core::HID::ControllerTriggerType type,
UpdateControllerAt(npad_type, npad_id, is_connected);
break;
case Core::HID::ControllerTriggerType::Battery: {
- if (!controller.is_connected) {
+ if (!controller.device->IsConnected()) {
return;
}
auto& shared_memory = controller.shared_memory_entry;
@@ -150,7 +150,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::SwitchProController;
break;
case Core::HID::NpadStyleIndex::Handheld:
@@ -166,21 +165,30 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
break;
case Core::HID::NpadStyleIndex::JoyconDual:
shared_memory.style_tag.joycon_dual.Assign(1);
- shared_memory.device_type.joycon_left.Assign(1);
- shared_memory.device_type.joycon_right.Assign(1);
- shared_memory.system_properties.is_vertical.Assign(1);
- shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.system_properties.use_minus.Assign(1);
+ if (controller.is_dual_left_connected) {
+ shared_memory.device_type.joycon_left.Assign(1);
+ shared_memory.system_properties.use_minus.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ shared_memory.device_type.joycon_right.Assign(1);
+ shared_memory.system_properties.use_plus.Assign(1);
+ }
shared_memory.system_properties.use_directional_buttons.Assign(1);
+ shared_memory.system_properties.is_vertical.Assign(1);
shared_memory.assignment_mode = NpadJoyAssignmentMode::Dual;
- shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+ if (controller.is_dual_left_connected && controller.is_dual_right_connected) {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDual;
+ } else if (controller.is_dual_left_connected) {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDualLeftOnly;
+ } else {
+ shared_memory.applet_footer.type = AppletFooterUiType::JoyDualRightOnly;
+ }
break;
case Core::HID::NpadStyleIndex::JoyconLeft:
shared_memory.style_tag.joycon_left.Assign(1);
shared_memory.device_type.joycon_left.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_minus.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyLeftHorizontal;
break;
case Core::HID::NpadStyleIndex::JoyconRight:
@@ -188,7 +196,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
shared_memory.device_type.joycon_right.Assign(1);
shared_memory.system_properties.is_horizontal.Assign(1);
shared_memory.system_properties.use_plus.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
shared_memory.applet_footer.type = AppletFooterUiType::JoyRightHorizontal;
break;
case Core::HID::NpadStyleIndex::GameCube:
@@ -200,7 +207,6 @@ void Controller_NPad::InitNewlyAddedController(Core::HID::NpadIdType npad_id) {
case Core::HID::NpadStyleIndex::Pokeball:
shared_memory.style_tag.palma.Assign(1);
shared_memory.device_type.palma.Assign(1);
- shared_memory.assignment_mode = NpadJoyAssignmentMode::Single;
break;
case Core::HID::NpadStyleIndex::NES:
shared_memory.style_tag.lark.Assign(1);
@@ -443,11 +449,15 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
case Core::HID::NpadStyleIndex::JoyconDual:
pad_state.connection_status.raw = 0;
pad_state.connection_status.is_connected.Assign(1);
- pad_state.connection_status.is_left_connected.Assign(1);
- pad_state.connection_status.is_right_connected.Assign(1);
+ if (controller.is_dual_left_connected) {
+ pad_state.connection_status.is_left_connected.Assign(1);
+ libnx_state.connection_status.is_left_connected.Assign(1);
+ }
+ if (controller.is_dual_right_connected) {
+ pad_state.connection_status.is_right_connected.Assign(1);
+ libnx_state.connection_status.is_right_connected.Assign(1);
+ }
- libnx_state.connection_status.is_left_connected.Assign(1);
- libnx_state.connection_status.is_right_connected.Assign(1);
pad_state.sampling_number =
npad.joy_dual_lifo.ReadCurrentEntry().state.sampling_number + 1;
npad.joy_dual_lifo.WriteNextEntry(pad_state);
@@ -687,7 +697,7 @@ Controller_NPad::NpadCommunicationMode Controller_NPad::GetNpadCommunicationMode
return communication_mode;
}
-void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
+void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
NpadJoyAssignmentMode assignment_mode) {
if (!IsNpadIdValid(npad_id)) {
LOG_ERROR(Service_HID, "Invalid NpadIdType npad_id:{}", npad_id);
@@ -698,6 +708,62 @@ void Controller_NPad::SetNpadMode(Core::HID::NpadIdType npad_id,
if (controller.shared_memory_entry.assignment_mode != assignment_mode) {
controller.shared_memory_entry.assignment_mode = assignment_mode;
}
+
+ if (!controller.device->IsConnected()) {
+ return;
+ }
+
+ if (assignment_mode == NpadJoyAssignmentMode::Dual) {
+ if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft) {
+ DisconnectNpad(npad_id);
+ controller.is_dual_left_connected = true;
+ controller.is_dual_right_connected = false;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ return;
+ }
+ if (controller.device->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) {
+ DisconnectNpad(npad_id);
+ controller.is_dual_left_connected = false;
+ controller.is_dual_right_connected = true;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id, true);
+ return;
+ }
+ return;
+ }
+
+ // This is for NpadJoyAssignmentMode::Single
+
+ // Only JoyconDual get affected by this function
+ if (controller.device->GetNpadStyleIndex() != Core::HID::NpadStyleIndex::JoyconDual) {
+ return;
+ }
+
+ if (controller.is_dual_left_connected && !controller.is_dual_right_connected) {
+ DisconnectNpad(npad_id);
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ return;
+ }
+ if (!controller.is_dual_left_connected && controller.is_dual_right_connected) {
+ DisconnectNpad(npad_id);
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ return;
+ }
+
+ // We have two controllers connected to the same npad_id we need to split them
+ const auto npad_id_2 = hid_core.GetFirstDisconnectedNpadId();
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ DisconnectNpad(npad_id);
+ if (npad_device_type == NpadJoyDeviceType::Left) {
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconLeft, npad_id, true);
+ controller_2.is_dual_left_connected = false;
+ controller_2.is_dual_right_connected = true;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
+ } else {
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconRight, npad_id, true);
+ controller_2.is_dual_left_connected = true;
+ controller_2.is_dual_right_connected = false;
+ UpdateControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_2, true);
+ }
}
bool Controller_NPad::VibrateControllerAtIndex(Core::HID::NpadIdType npad_id,
@@ -907,6 +973,7 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
}
auto& shared_memory_entry = controller.shared_memory_entry;
+ // Don't reset shared_memory_entry.assignment_mode this value is persistent
shared_memory_entry.style_tag.raw = Core::HID::NpadStyleSet::None; // Zero out
shared_memory_entry.device_type.raw = 0;
shared_memory_entry.system_properties.raw = 0;
@@ -923,9 +990,10 @@ void Controller_NPad::DisconnectNpad(Core::HID::NpadIdType npad_id) {
.left = {},
.right = {},
};
- shared_memory_entry.assignment_mode = NpadJoyAssignmentMode::Dual;
shared_memory_entry.applet_footer.type = AppletFooterUiType::None;
+ controller.is_dual_left_connected = true;
+ controller.is_dual_right_connected = true;
controller.is_connected = false;
controller.device->Disconnect();
SignalStyleSetChangedEvent(npad_id);
@@ -1022,19 +1090,70 @@ void Controller_NPad::MergeSingleJoyAsDualJoy(Core::HID::NpadIdType npad_id_1,
npad_id_2);
return;
}
- auto& controller_1 = GetControllerFromNpadIdType(npad_id_1).device;
- auto& controller_2 = GetControllerFromNpadIdType(npad_id_2).device;
+ auto& controller_1 = GetControllerFromNpadIdType(npad_id_1);
+ auto& controller_2 = GetControllerFromNpadIdType(npad_id_2);
+ const auto controller_style_1 = controller_1.device->GetNpadStyleIndex();
+ const auto controller_style_2 = controller_2.device->GetNpadStyleIndex();
+ bool merge_controllers = false;
// If the controllers at both npad indices form a pair of left and right joycons, merge them.
// Otherwise, do nothing.
- if ((controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
- controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight) ||
- (controller_2->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconLeft &&
- controller_1->GetNpadStyleIndex() == Core::HID::NpadStyleIndex::JoyconRight)) {
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconRight &&
+ controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconRight &&
+ controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_1 == Core::HID::NpadStyleIndex::JoyconLeft &&
+ !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_1.is_dual_left_connected && !controller_1.is_dual_right_connected &&
+ !controller_2.is_dual_left_connected && controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+ if (controller_style_1 == Core::HID::NpadStyleIndex::JoyconDual &&
+ controller_style_2 == Core::HID::NpadStyleIndex::JoyconDual &&
+ !controller_1.is_dual_left_connected && controller_1.is_dual_right_connected &&
+ controller_2.is_dual_left_connected && !controller_2.is_dual_right_connected) {
+ merge_controllers = true;
+ }
+
+ if (merge_controllers) {
// Disconnect the joycon at the second id and connect the dual joycon at the first index.
DisconnectNpad(npad_id_2);
+ controller_1.is_dual_left_connected = true;
+ controller_1.is_dual_right_connected = true;
AddNewControllerAt(Core::HID::NpadStyleIndex::JoyconDual, npad_id_1);
+ return;
}
+ LOG_WARNING(Service_HID,
+ "Controllers can't be merged npad_id_1:{}, npad_id_2:{}, type_1:{}, type_2:{}, "
+ "dual_1(left/right):{}/{}, dual_2(left/right):{}/{}",
+ npad_id_1, npad_id_2, controller_1.device->GetNpadStyleIndex(),
+ controller_2.device->GetNpadStyleIndex(), controller_1.is_dual_left_connected,
+ controller_1.is_dual_right_connected, controller_2.is_dual_left_connected,
+ controller_2.is_dual_right_connected);
}
void Controller_NPad::StartLRAssignmentMode() {
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index de5fa5a64..63281cb35 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -113,7 +113,8 @@ public:
void SetNpadCommunicationMode(NpadCommunicationMode communication_mode_);
NpadCommunicationMode GetNpadCommunicationMode() const;
- void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyAssignmentMode assignment_mode);
+ void SetNpadMode(Core::HID::NpadIdType npad_id, NpadJoyDeviceType npad_device_type,
+ NpadJoyAssignmentMode assignment_mode);
bool VibrateControllerAtIndex(Core::HID::NpadIdType npad_id, std::size_t device_index,
const Core::HID::VibrationValue& vibration_value);
@@ -464,7 +465,10 @@ private:
std::array<VibrationData, 2> vibration{};
bool unintended_home_button_input_protection{};
bool is_connected{};
- Core::HID::NpadStyleIndex npad_type{Core::HID::NpadStyleIndex::None};
+
+ // Dual joycons can have only one side connected
+ bool is_dual_left_connected{true};
+ bool is_dual_right_connected{true};
// Motion parameters
bool sixaxis_at_rest{true};
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 14f737d39..7163e1a4e 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -975,35 +975,35 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyDeviceType::Left,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
}
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
- // TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
struct Parameters {
Core::HID::NpadIdType npad_id;
INSERT_PADDING_WORDS_NOINIT(1);
u64 applet_resource_user_id;
- u64 npad_joy_device_type;
+ Controller_NPad::NpadJoyDeviceType npad_joy_device_type;
};
static_assert(sizeof(Parameters) == 0x18, "Parameters has incorrect size.");
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Single);
+ .SetNpadMode(parameters.npad_id, parameters.npad_joy_device_type,
+ Controller_NPad::NpadJoyAssignmentMode::Single);
- LOG_WARNING(Service_HID,
- "(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
- parameters.npad_id, parameters.applet_resource_user_id,
- parameters.npad_joy_device_type);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
+ parameters.npad_id, parameters.applet_resource_user_id,
+ parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
@@ -1021,10 +1021,10 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetNpadMode(parameters.npad_id, Controller_NPad::NpadJoyAssignmentMode::Dual);
+ .SetNpadMode(parameters.npad_id, {}, Controller_NPad::NpadJoyAssignmentMode::Dual);
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
- parameters.npad_id, parameters.applet_resource_user_id);
+ LOG_INFO(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ResultSuccess);
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index 0d7d4ad03..8314d1ec2 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -20,8 +20,12 @@ NvResult nvhost_nvdec::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>&
switch (command.group) {
case 0x0:
switch (command.cmd) {
- case 0x1:
- return Submit(input, output);
+ case 0x1: {
+ if (!fd_to_id.contains(fd)) {
+ fd_to_id[fd] = next_id++;
+ }
+ return Submit(fd, input, output);
+ }
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -66,7 +70,10 @@ void nvhost_nvdec::OnOpen(DeviceFD fd) {}
void nvhost_nvdec::OnClose(DeviceFD fd) {
LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
- system.GPU().ClearCdmaInstance();
+ const auto iter = fd_to_id.find(fd);
+ if (iter != fd_to_id.end()) {
+ system.GPU().ClearCdmaInstance(iter->second);
+ }
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
index 523d96e3a..a507c4d0a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -24,6 +24,9 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+
+private:
+ u32 next_id{};
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
index e61261f98..8a05f0668 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -59,7 +59,8 @@ NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
return NvResult::Success;
}
-NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvdec_common::Submit(DeviceFD fd, const std::vector<u8>& input,
+ std::vector<u8>& output) {
IoctlSubmit params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
@@ -93,7 +94,7 @@ NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u
Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
system.Memory().ReadBlock(object->addr + cmd_buffer.offset, cmdlist.data(),
cmdlist.size() * sizeof(u32));
- gpu.PushCommandBuffer(cmdlist);
+ gpu.PushCommandBuffer(fd_to_id[fd], cmdlist);
}
std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
// Some games expect command_buffers to be written back
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
index 351625c17..e28c54df6 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -104,13 +104,14 @@ protected:
/// Ioctl command implementations
NvResult SetNVMAPfd(const std::vector<u8>& input);
- NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Submit(DeviceFD fd, const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+ std::unordered_map<DeviceFD, u32> fd_to_id{};
s32_le nvmap_fd{};
u32_le submit_timeout{};
std::shared_ptr<nvmap> nvmap_dev;
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
index eac4dd530..76b39806f 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -21,7 +21,10 @@ NvResult nvhost_vic::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& i
case 0x0:
switch (command.cmd) {
case 0x1:
- return Submit(input, output);
+ if (!fd_to_id.contains(fd)) {
+ fd_to_id[fd] = next_id++;
+ }
+ return Submit(fd, input, output);
case 0x2:
return GetSyncpoint(input, output);
case 0x3:
@@ -65,7 +68,10 @@ NvResult nvhost_vic::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& i
void nvhost_vic::OnOpen(DeviceFD fd) {}
void nvhost_vic::OnClose(DeviceFD fd) {
- system.GPU().ClearCdmaInstance();
+ const auto iter = fd_to_id.find(fd);
+ if (iter != fd_to_id.end()) {
+ system.GPU().ClearCdmaInstance(iter->second);
+ }
}
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
index 6d7fda9d1..c9732c037 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -23,5 +23,8 @@ public:
void OnOpen(DeviceFD fd) override;
void OnClose(DeviceFD fd) override;
+
+private:
+ u32 next_id{};
};
} // namespace Service::Nvidia::Devices