diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/k_page_table.cpp | 17 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_page_table.h | 3 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_transfer_memory.cpp | 89 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_transfer_memory.h | 14 | ||||
| -rw-r--r-- | src/core/hle/kernel/svc/svc_transfer_memory.cpp | 54 |
5 files changed, 160 insertions, 17 deletions
diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 5b51edf30..1fbfbf31f 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp | |||
| @@ -2949,6 +2949,23 @@ Result KPageTable::UnlockForIpcUserBuffer(KProcessAddress address, size_t size) | |||
| 2949 | KMemoryAttribute::Locked, nullptr)); | 2949 | KMemoryAttribute::Locked, nullptr)); |
| 2950 | } | 2950 | } |
| 2951 | 2951 | ||
| 2952 | Result KPageTable::LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 2953 | KMemoryPermission perm) { | ||
| 2954 | R_RETURN(this->LockMemoryAndOpen(out, nullptr, address, size, KMemoryState::FlagCanTransfer, | ||
| 2955 | KMemoryState::FlagCanTransfer, KMemoryPermission::All, | ||
| 2956 | KMemoryPermission::UserReadWrite, KMemoryAttribute::All, | ||
| 2957 | KMemoryAttribute::None, perm, KMemoryAttribute::Locked)); | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | Result KPageTable::UnlockForTransferMemory(KProcessAddress address, size_t size, | ||
| 2961 | const KPageGroup& pg) { | ||
| 2962 | R_RETURN(this->UnlockMemory(address, size, KMemoryState::FlagCanTransfer, | ||
| 2963 | KMemoryState::FlagCanTransfer, KMemoryPermission::None, | ||
| 2964 | KMemoryPermission::None, KMemoryAttribute::All, | ||
| 2965 | KMemoryAttribute::Locked, KMemoryPermission::UserReadWrite, | ||
| 2966 | KMemoryAttribute::Locked, std::addressof(pg))); | ||
| 2967 | } | ||
| 2968 | |||
| 2952 | Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { | 2969 | Result KPageTable::LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size) { |
| 2953 | R_RETURN(this->LockMemoryAndOpen( | 2970 | R_RETURN(this->LockMemoryAndOpen( |
| 2954 | out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, | 2971 | out, nullptr, addr, size, KMemoryState::FlagCanCodeMemory, KMemoryState::FlagCanCodeMemory, |
diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index b9e8c6042..7da675f27 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h | |||
| @@ -104,6 +104,9 @@ public: | |||
| 104 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); | 104 | Result CleanupForIpcServer(KProcessAddress address, size_t size, KMemoryState dst_state); |
| 105 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); | 105 | Result CleanupForIpcClient(KProcessAddress address, size_t size, KMemoryState dst_state); |
| 106 | 106 | ||
| 107 | Result LockForTransferMemory(KPageGroup* out, KProcessAddress address, size_t size, | ||
| 108 | KMemoryPermission perm); | ||
| 109 | Result UnlockForTransferMemory(KProcessAddress address, size_t size, const KPageGroup& pg); | ||
| 107 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size); | 110 | Result LockForCodeMemory(KPageGroup* out, KProcessAddress addr, size_t size); |
| 108 | Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg); | 111 | Result UnlockForCodeMemory(KProcessAddress addr, size_t size, const KPageGroup& pg); |
| 109 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, | 112 | Result MakeAndOpenPageGroup(KPageGroup* out, KProcessAddress address, size_t num_pages, |
diff --git a/src/core/hle/kernel/k_transfer_memory.cpp b/src/core/hle/kernel/k_transfer_memory.cpp index 13d34125c..0e2e11743 100644 --- a/src/core/hle/kernel/k_transfer_memory.cpp +++ b/src/core/hle/kernel/k_transfer_memory.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project | 1 | // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project |
| 2 | // SPDX-License-Identifier: GPL-2.0-or-later | 2 | // SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | 3 | ||
| 4 | #include "common/scope_exit.h" | ||
| 4 | #include "core/hle/kernel/k_process.h" | 5 | #include "core/hle/kernel/k_process.h" |
| 5 | #include "core/hle/kernel/k_resource_limit.h" | 6 | #include "core/hle/kernel/k_resource_limit.h" |
| 6 | #include "core/hle/kernel/k_transfer_memory.h" | 7 | #include "core/hle/kernel/k_transfer_memory.h" |
| @@ -9,28 +10,50 @@ | |||
| 9 | namespace Kernel { | 10 | namespace Kernel { |
| 10 | 11 | ||
| 11 | KTransferMemory::KTransferMemory(KernelCore& kernel) | 12 | KTransferMemory::KTransferMemory(KernelCore& kernel) |
| 12 | : KAutoObjectWithSlabHeapAndContainer{kernel} {} | 13 | : KAutoObjectWithSlabHeapAndContainer{kernel}, m_lock{kernel} {} |
| 13 | 14 | ||
| 14 | KTransferMemory::~KTransferMemory() = default; | 15 | KTransferMemory::~KTransferMemory() = default; |
| 15 | 16 | ||
| 16 | Result KTransferMemory::Initialize(KProcessAddress address, std::size_t size, | 17 | Result KTransferMemory::Initialize(KProcessAddress addr, std::size_t size, |
| 17 | Svc::MemoryPermission owner_perm) { | 18 | Svc::MemoryPermission own_perm) { |
| 18 | // Set members. | 19 | // Set members. |
| 19 | m_owner = GetCurrentProcessPointer(m_kernel); | 20 | m_owner = GetCurrentProcessPointer(m_kernel); |
| 20 | 21 | ||
| 21 | // TODO(bunnei): Lock for transfer memory | 22 | // Get the owner page table. |
| 23 | auto& page_table = m_owner->GetPageTable(); | ||
| 24 | |||
| 25 | // Construct the page group, guarding to make sure our state is valid on exit. | ||
| 26 | m_page_group.emplace(m_kernel, page_table.GetBlockInfoManager()); | ||
| 27 | auto pg_guard = SCOPE_GUARD({ m_page_group.reset(); }); | ||
| 28 | |||
| 29 | // Lock the memory. | ||
| 30 | R_TRY(page_table.LockForTransferMemory(std::addressof(*m_page_group), addr, size, | ||
| 31 | ConvertToKMemoryPermission(own_perm))); | ||
| 22 | 32 | ||
| 23 | // Set remaining tracking members. | 33 | // Set remaining tracking members. |
| 24 | m_owner->Open(); | 34 | m_owner->Open(); |
| 25 | m_owner_perm = owner_perm; | 35 | m_owner_perm = own_perm; |
| 26 | m_address = address; | 36 | m_address = addr; |
| 27 | m_size = size; | ||
| 28 | m_is_initialized = true; | 37 | m_is_initialized = true; |
| 38 | m_is_mapped = false; | ||
| 29 | 39 | ||
| 40 | // We succeeded. | ||
| 41 | pg_guard.Cancel(); | ||
| 30 | R_SUCCEED(); | 42 | R_SUCCEED(); |
| 31 | } | 43 | } |
| 32 | 44 | ||
| 33 | void KTransferMemory::Finalize() {} | 45 | void KTransferMemory::Finalize() { |
| 46 | // Unlock. | ||
| 47 | if (!m_is_mapped) { | ||
| 48 | const size_t size = m_page_group->GetNumPages() * PageSize; | ||
| 49 | ASSERT(R_SUCCEEDED( | ||
| 50 | m_owner->GetPageTable().UnlockForTransferMemory(m_address, size, *m_page_group))); | ||
| 51 | } | ||
| 52 | |||
| 53 | // Close the page group. | ||
| 54 | m_page_group->Close(); | ||
| 55 | m_page_group->Finalize(); | ||
| 56 | } | ||
| 34 | 57 | ||
| 35 | void KTransferMemory::PostDestroy(uintptr_t arg) { | 58 | void KTransferMemory::PostDestroy(uintptr_t arg) { |
| 36 | KProcess* owner = reinterpret_cast<KProcess*>(arg); | 59 | KProcess* owner = reinterpret_cast<KProcess*>(arg); |
| @@ -38,4 +61,54 @@ void KTransferMemory::PostDestroy(uintptr_t arg) { | |||
| 38 | owner->Close(); | 61 | owner->Close(); |
| 39 | } | 62 | } |
| 40 | 63 | ||
| 64 | Result KTransferMemory::Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm) { | ||
| 65 | // Validate the size. | ||
| 66 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 67 | |||
| 68 | // Validate the permission. | ||
| 69 | R_UNLESS(m_owner_perm == map_perm, ResultInvalidState); | ||
| 70 | |||
| 71 | // Lock ourselves. | ||
| 72 | KScopedLightLock lk(m_lock); | ||
| 73 | |||
| 74 | // Ensure we're not already mapped. | ||
| 75 | R_UNLESS(!m_is_mapped, ResultInvalidState); | ||
| 76 | |||
| 77 | // Map the memory. | ||
| 78 | const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) | ||
| 79 | ? KMemoryState::Transfered | ||
| 80 | : KMemoryState::SharedTransfered; | ||
| 81 | R_TRY(GetCurrentProcess(m_kernel).GetPageTable().MapPageGroup( | ||
| 82 | address, *m_page_group, state, KMemoryPermission::UserReadWrite)); | ||
| 83 | |||
| 84 | // Mark ourselves as mapped. | ||
| 85 | m_is_mapped = true; | ||
| 86 | |||
| 87 | R_SUCCEED(); | ||
| 88 | } | ||
| 89 | |||
| 90 | Result KTransferMemory::Unmap(KProcessAddress address, size_t size) { | ||
| 91 | // Validate the size. | ||
| 92 | R_UNLESS(m_page_group->GetNumPages() == Common::DivideUp(size, PageSize), ResultInvalidSize); | ||
| 93 | |||
| 94 | // Lock ourselves. | ||
| 95 | KScopedLightLock lk(m_lock); | ||
| 96 | |||
| 97 | // Unmap the memory. | ||
| 98 | const KMemoryState state = (m_owner_perm == Svc::MemoryPermission::None) | ||
| 99 | ? KMemoryState::Transfered | ||
| 100 | : KMemoryState::SharedTransfered; | ||
| 101 | R_TRY(GetCurrentProcess(m_kernel).GetPageTable().UnmapPageGroup(address, *m_page_group, state)); | ||
| 102 | |||
| 103 | // Mark ourselves as unmapped. | ||
| 104 | ASSERT(m_is_mapped); | ||
| 105 | m_is_mapped = false; | ||
| 106 | |||
| 107 | R_SUCCEED(); | ||
| 108 | } | ||
| 109 | |||
| 110 | size_t KTransferMemory::GetSize() const { | ||
| 111 | return m_is_initialized ? m_page_group->GetNumPages() * PageSize : 0; | ||
| 112 | } | ||
| 113 | |||
| 41 | } // namespace Kernel | 114 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/k_transfer_memory.h b/src/core/hle/kernel/k_transfer_memory.h index 54f97ccb4..8a0b08761 100644 --- a/src/core/hle/kernel/k_transfer_memory.h +++ b/src/core/hle/kernel/k_transfer_memory.h | |||
| @@ -3,6 +3,9 @@ | |||
| 3 | 3 | ||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <optional> | ||
| 7 | |||
| 8 | #include "core/hle/kernel/k_page_group.h" | ||
| 6 | #include "core/hle/kernel/slab_helpers.h" | 9 | #include "core/hle/kernel/slab_helpers.h" |
| 7 | #include "core/hle/kernel/svc_types.h" | 10 | #include "core/hle/kernel/svc_types.h" |
| 8 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| @@ -48,16 +51,19 @@ public: | |||
| 48 | return m_address; | 51 | return m_address; |
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | size_t GetSize() const { | 54 | size_t GetSize() const; |
| 52 | return m_is_initialized ? m_size : 0; | 55 | |
| 53 | } | 56 | Result Map(KProcessAddress address, size_t size, Svc::MemoryPermission map_perm); |
| 57 | Result Unmap(KProcessAddress address, size_t size); | ||
| 54 | 58 | ||
| 55 | private: | 59 | private: |
| 60 | std::optional<KPageGroup> m_page_group{}; | ||
| 56 | KProcess* m_owner{}; | 61 | KProcess* m_owner{}; |
| 57 | KProcessAddress m_address{}; | 62 | KProcessAddress m_address{}; |
| 63 | KLightLock m_lock; | ||
| 58 | Svc::MemoryPermission m_owner_perm{}; | 64 | Svc::MemoryPermission m_owner_perm{}; |
| 59 | size_t m_size{}; | ||
| 60 | bool m_is_initialized{}; | 65 | bool m_is_initialized{}; |
| 66 | bool m_is_mapped{}; | ||
| 61 | }; | 67 | }; |
| 62 | 68 | ||
| 63 | } // namespace Kernel | 69 | } // namespace Kernel |
diff --git a/src/core/hle/kernel/svc/svc_transfer_memory.cpp b/src/core/hle/kernel/svc/svc_transfer_memory.cpp index 7d94e7f09..1f97121b3 100644 --- a/src/core/hle/kernel/svc/svc_transfer_memory.cpp +++ b/src/core/hle/kernel/svc/svc_transfer_memory.cpp | |||
| @@ -71,15 +71,59 @@ Result CreateTransferMemory(Core::System& system, Handle* out, u64 address, u64 | |||
| 71 | } | 71 | } |
| 72 | 72 | ||
| 73 | Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, | 73 | Result MapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, uint64_t size, |
| 74 | MemoryPermission owner_perm) { | 74 | MemoryPermission map_perm) { |
| 75 | UNIMPLEMENTED(); | 75 | // Validate the address/size. |
| 76 | R_THROW(ResultNotImplemented); | 76 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); |
| 77 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 78 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 79 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 80 | |||
| 81 | // Validate the permission. | ||
| 82 | R_UNLESS(IsValidTransferMemoryPermission(map_perm), ResultInvalidState); | ||
| 83 | |||
| 84 | // Get the transfer memory. | ||
| 85 | KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) | ||
| 86 | .GetHandleTable() | ||
| 87 | .GetObject<KTransferMemory>(trmem_handle); | ||
| 88 | R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); | ||
| 89 | |||
| 90 | // Verify that the mapping is in range. | ||
| 91 | R_UNLESS(GetCurrentProcess(system.Kernel()) | ||
| 92 | .GetPageTable() | ||
| 93 | .CanContain(address, size, KMemoryState::Transfered), | ||
| 94 | ResultInvalidMemoryRegion); | ||
| 95 | |||
| 96 | // Map the transfer memory. | ||
| 97 | R_TRY(trmem->Map(address, size, map_perm)); | ||
| 98 | |||
| 99 | // We succeeded. | ||
| 100 | R_SUCCEED(); | ||
| 77 | } | 101 | } |
| 78 | 102 | ||
| 79 | Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, | 103 | Result UnmapTransferMemory(Core::System& system, Handle trmem_handle, uint64_t address, |
| 80 | uint64_t size) { | 104 | uint64_t size) { |
| 81 | UNIMPLEMENTED(); | 105 | // Validate the address/size. |
| 82 | R_THROW(ResultNotImplemented); | 106 | R_UNLESS(Common::IsAligned(address, PageSize), ResultInvalidAddress); |
| 107 | R_UNLESS(Common::IsAligned(size, PageSize), ResultInvalidSize); | ||
| 108 | R_UNLESS(size > 0, ResultInvalidSize); | ||
| 109 | R_UNLESS((address < address + size), ResultInvalidCurrentMemory); | ||
| 110 | |||
| 111 | // Get the transfer memory. | ||
| 112 | KScopedAutoObject trmem = GetCurrentProcess(system.Kernel()) | ||
| 113 | .GetHandleTable() | ||
| 114 | .GetObject<KTransferMemory>(trmem_handle); | ||
| 115 | R_UNLESS(trmem.IsNotNull(), ResultInvalidHandle); | ||
| 116 | |||
| 117 | // Verify that the mapping is in range. | ||
| 118 | R_UNLESS(GetCurrentProcess(system.Kernel()) | ||
| 119 | .GetPageTable() | ||
| 120 | .CanContain(address, size, KMemoryState::Transfered), | ||
| 121 | ResultInvalidMemoryRegion); | ||
| 122 | |||
| 123 | // Unmap the transfer memory. | ||
| 124 | R_TRY(trmem->Unmap(address, size)); | ||
| 125 | |||
| 126 | R_SUCCEED(); | ||
| 83 | } | 127 | } |
| 84 | 128 | ||
| 85 | Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, | 129 | Result MapTransferMemory64(Core::System& system, Handle trmem_handle, uint64_t address, |