diff options
Diffstat (limited to 'src/core')
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, ¶m_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(¶ms, 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(), ¶ms, 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 |