diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt index 9de5b996..b2cf92cb 100644 --- a/app/CMakeLists.txt +++ b/app/CMakeLists.txt @@ -174,7 +174,9 @@ add_library(skyline SHARED ${source_DIR}/skyline/kernel/svc.cpp ${source_DIR}/skyline/kernel/types/KProcess.cpp ${source_DIR}/skyline/kernel/types/KThread.cpp + ${source_DIR}/skyline/kernel/types/KTransferMemory.cpp ${source_DIR}/skyline/kernel/types/KSharedMemory.cpp + ${source_DIR}/skyline/kernel/types/KMemory.cpp ${source_DIR}/skyline/kernel/types/KSyncObject.cpp ${source_DIR}/skyline/audio.cpp ${source_DIR}/skyline/gpu.cpp diff --git a/app/src/main/cpp/skyline/kernel/svc.cpp b/app/src/main/cpp/skyline/kernel/svc.cpp index d705b076..69646055 100644 --- a/app/src/main/cpp/skyline/kernel/svc.cpp +++ b/app/src/main/cpp/skyline/kernel/svc.cpp @@ -577,8 +577,11 @@ namespace skyline::kernel::svc { return; } - auto tmem{state.process->NewHandle(address, size, permission)}; - state.process->memory.AddRef(tmem.item); + auto tmem{state.process->NewHandle(size)}; + if (!tmem.item->Map(span{address, size}, permission)) [[unlikely]] { + state.ctx->gpr.w0 = result::InvalidState; + return; + } Logger::Debug("Creating transfer memory (0x{:X}) at 0x{:X} - 0x{:X} (0x{:X} bytes) ({}{}{})", tmem.handle, address, address + size, size, permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-'); diff --git a/app/src/main/cpp/skyline/kernel/types/KMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KMemory.cpp new file mode 100644 index 00000000..a4184899 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/types/KMemory.cpp @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include +#include +#include +#include "KMemory.h" +#include "KProcess.h" + +namespace skyline::kernel::type { + KMemory::KMemory(const DeviceState &state, KType objectType, size_t size) : KObject(state, objectType), guest() { + fd = ASharedMemory_create(objectType == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size); + if (fd < 0) [[unlikely]] + throw exception("An error occurred while creating shared memory: {}", fd); + + u8 *hostPtr{static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))}; + if (hostPtr == MAP_FAILED) [[unlikely]] + throw exception("An occurred while mapping shared memory: {}", strerror(errno)); + + host = span{hostPtr, size}; + } + + u8 *KMemory::Map(span map, memory::Permission permission) { + if (!state.process->memory.AddressSpaceContains(map)) [[unlikely]] + throw exception("KMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); + if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) [[unlikely]] + throw exception("KMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size()); + if (guest.valid()) [[unlikely]] + throw exception("Mapping KMemory multiple times on guest is not supported: Requested Mapping: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size(), guest.data(), guest.end().base(), guest.size()); + + if (mmap(map.data(), map.size(), permission.Get() ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE, MAP_SHARED | (map.data() ? MAP_FIXED : 0), fd, 0) == MAP_FAILED) [[unlikely]] + throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno)); + guest = map; + + return guest.data(); + } + + void KMemory::Unmap(span map) { + if (!state.process->memory.AddressSpaceContains(map)) [[unlikely]] + throw exception("KMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); + if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) [[unlikely]] + throw exception("KMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} ({} bytes)", map.data(), map.end().base(), map.size()); + if (guest.data() != map.data() && guest.size() != map.size()) [[unlikely]] + throw exception("Unmapping KMemory partially is not supported: Requested Unmap: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size(), guest.data(), guest.end().base(), guest.size()); + + if (mmap(map.data(), map.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] + throw exception("An error occurred while unmapping shared/transfer memory in guest: {}", strerror(errno)); + } + + KMemory::~KMemory() { + if (host.valid()) + munmap(host.data(), host.size()); + + close(fd); + } +} \ No newline at end of file diff --git a/app/src/main/cpp/skyline/kernel/types/KMemory.h b/app/src/main/cpp/skyline/kernel/types/KMemory.h index 72513079..48b3e429 100644 --- a/app/src/main/cpp/skyline/kernel/types/KMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KMemory.h @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once @@ -8,17 +8,31 @@ namespace skyline::kernel::type { /** - * @brief The base kernel memory object that other memory classes derieve from + * @brief The base kernel shared memory object that other memory classes derieve from */ class KMemory : public KObject { + private: + int fd; //!< A file descriptor to the underlying shared memory + public: - KMemory(const DeviceState &state, KType objectType, span guest) : KObject(state, objectType), guest(guest) {} + KMemory(const DeviceState &state, KType objectType, size_t size); /** * @return A span representing the memory object on the guest */ span guest; + span host; //!< We also keep a host mirror of the underlying shared memory for host access, it is persistently mapped and should be used by anything accessing the memory on the host - virtual ~KMemory() = default; + /** + * @note 'ptr' needs to be in guest-reserved address space + */ + virtual u8 *Map(span map, memory::Permission permission); + + /** + * @note 'ptr' needs to be in guest-reserved address space + */ + virtual void Unmap(span map); + + virtual ~KMemory(); }; } diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp index cdcafa4e..3091c358 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.cpp @@ -1,78 +1,34 @@ // SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) -#include -#include -#include #include "KSharedMemory.h" #include "KProcess.h" namespace skyline::kernel::type { - KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, KType type) - : KMemory(state, type, span{}) { - fd = ASharedMemory_create(type == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size); - if (fd < 0) [[unlikely]] - throw exception("An error occurred while creating shared memory: {}", fd); - - u8 *hostPtr{static_cast(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))}; - if (hostPtr == MAP_FAILED) [[unlikely]] - throw exception("An occurred while mapping shared memory: {}", strerror(errno)); - - host = span{hostPtr, size}; - } + KSharedMemory::KSharedMemory(const DeviceState &state, size_t size) + : KMemory(state, KType::KSharedMemory, size) {} u8 *KSharedMemory::Map(span map, memory::Permission permission) { - if (!state.process->memory.AddressSpaceContains(map)) [[unlikely]] - throw exception("KSharedMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); - if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) [[unlikely]] - throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size()); - if (guest.valid()) [[unlikely]] - throw exception("Mapping KSharedMemory multiple times on guest is not supported: Requested Mapping: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size(), guest.data(), guest.end().base(), guest.size()); + u8 *result{KMemory::Map(map, permission)}; - auto guestPtr{static_cast(mmap(map.data(), map.size(), permission.Get(), MAP_SHARED | (map.data() ? MAP_FIXED : 0), fd, 0))}; - if (guestPtr == MAP_FAILED) [[unlikely]] - throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno)); - guest = span{guestPtr, map.size()}; + state.process->memory.MapSharedMemory(guest, permission); - if (objectType == KType::KTransferMemory) { - state.process->memory.MapTransferMemory(guest, permission); - state.process->memory.SetLockOnChunks(guest, true); - } else { - state.process->memory.MapSharedMemory(guest, permission); - } - - return guest.data(); + return result; } void KSharedMemory::Unmap(span map) { - auto &memoryManager{state.process->memory}; - if (!memoryManager.AddressSpaceContains(map)) [[unlikely]] - throw exception("KSharedMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", map.data(), map.end().base()); - if (!util::IsPageAligned(map.data()) || !util::IsPageAligned(map.size())) [[unlikely]] - throw exception("KSharedMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} ({} bytes)", map.data(), map.end().base(), map.size()); - if (guest.data() != map.data() && guest.size() != map.size()) [[unlikely]] - throw exception("Unmapping KSharedMemory partially is not supported: Requested Unmap: 0x{:X} - 0x{:X} (0x{:X}), Current Mapping: 0x{:X} - 0x{:X} (0x{:X})", map.data(), map.end().base(), map.size(), guest.data(), guest.end().base(), guest.size()); - - if (mmap(map.data(), map.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] - throw exception("An error occurred while unmapping shared memory in guest: {}", strerror(errno)); + KMemory::Unmap(map); guest = span{}; - memoryManager.UnmapMemory(map); + state.process->memory.UnmapMemory(map); } KSharedMemory::~KSharedMemory() { if (state.process && guest.valid()) { - auto &memoryManager{state.process->memory}; - if (mmap(guest.data(), guest.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] Logger::Warn("An error occurred while unmapping shared memory: {}", strerror(errno)); state.process->memory.UnmapMemory(guest); } - - if (host.valid()) - munmap(host.data(), host.size()); - - close(fd); } } diff --git a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h index 724329fd..6666dcc2 100644 --- a/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KSharedMemory.h @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once @@ -10,13 +10,8 @@ namespace skyline::kernel::type { * @brief KSharedMemory is used to retain two mappings of the same underlying memory, allowing sharing memory between two processes */ class KSharedMemory : public KMemory { - private: - int fd; //!< A file descriptor to the underlying shared memory - public: - span host; //!< We also keep a host mirror of the underlying shared memory for host access, it is persistently mapped and should be used by anything accessing the memory on the host - - KSharedMemory(const DeviceState &state, size_t size, KType type = KType::KSharedMemory); + KSharedMemory(const DeviceState &state, size_t size); /** * @note 'ptr' needs to be in guest-reserved address space diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp new file mode 100644 index 00000000..f37fcc53 --- /dev/null +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) + +#include "KTransferMemory.h" +#include "KProcess.h" + +namespace skyline::kernel::type { + KTransferMemory::KTransferMemory(const DeviceState &state, size_t size) + : KMemory(state, KType::KTransferMemory, size) {} + + u8 *KTransferMemory::Map(span map, memory::Permission permission) { + std::memcpy(host.data(), map.data(), map.size()); + u8 *result{KMemory::Map(map, permission)}; + + auto oldChunk{state.process->memory.GetChunk(map.data()).value()}; + + originalMapping = oldChunk.second; + + if (!originalMapping.state.transferMemoryAllowed) [[unlikely]] { + Logger::Warn("Tried to map transfer memory with incompatible state at: 0x{:X} (0x{:X} bytes)", map.data(), map.size()); + return nullptr; + } else { + state.process->memory.MapTransferMemory(guest, permission); + state.process->memory.SetLockOnChunks(guest, true); + return result; + } + } + + void KTransferMemory::Unmap(span map) { + KMemory::Unmap(map); + + guest = span{}; + switch (originalMapping.state.type) { + case memory::MemoryType::CodeMutable: + state.process->memory.MapMutableCodeMemory(map); + break; + case memory::MemoryType::Heap: + state.process->memory.MapHeapMemory(map); + break; + default: + Logger::Warn("Unmapping KTransferMemory with incompatible state: (0x{:X})", originalMapping.state.value); + } + std::memcpy(map.data(), host.data(), map.size()); + } + + KTransferMemory::~KTransferMemory() { + if (state.process && guest.valid()) { + if (mmap(guest.data(), guest.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0) == MAP_FAILED) [[unlikely]] + Logger::Warn("An error occurred while unmapping transfer memory in guest: {}", strerror(errno)); + + switch (originalMapping.state.type) { + case memory::MemoryType::CodeMutable: + state.process->memory.MapMutableCodeMemory(guest); + break; + case memory::MemoryType::Heap: + state.process->memory.MapHeapMemory(guest); + break; + default: + Logger::Warn("Unmapping KTransferMemory with incompatible state: (0x{:X})", originalMapping.state.value); + } + std::memcpy(guest.data(), host.data(), guest.size()); + } + } +} diff --git a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h index 63a9df55..073717e9 100644 --- a/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h +++ b/app/src/main/cpp/skyline/kernel/types/KTransferMemory.h @@ -1,24 +1,35 @@ // SPDX-License-Identifier: MPL-2.0 -// Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/) #pragma once -#include "KSharedMemory.h" +#include "KMemory.h" namespace skyline::kernel::type { /** * @brief KTransferMemory is used to transfer memory from one application to another on HOS, we emulate this abstraction using KSharedMemory as it's essentially the same with the main difference being that KSharedMemory is allocated by the kernel while KTransferMemory is created from memory that's been allocated by the guest beforehand * @note KSharedMemory::{Map, Unmap, ~KSharedMemory} contains code to handle differences in memory attributes and destruction */ - class KTransferMemory : public KSharedMemory { + class KTransferMemory : public KMemory { + private: + ChunkDescriptor originalMapping; + public: /** * @note 'ptr' needs to be in guest-reserved address space */ - KTransferMemory(const DeviceState &state, u8 *ptr, size_t size, memory::Permission permission) - : KSharedMemory(state, size, KType::KTransferMemory) { - std::memcpy(host.data(), ptr, size); - Map(span{ptr, size}, permission); - } + KTransferMemory(const DeviceState &state, size_t size); + + /** + * @note 'ptr' needs to be in guest-reserved address space + */ + u8 *Map(span map, memory::Permission permission); + + /** + * @note 'ptr' needs to be in guest-reserved address space + */ + void Unmap(span map); + + ~KTransferMemory(); }; }