mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-16 07:19:19 +01:00
Implement KTransferMemory correctly
This commit is contained in:
parent
dac180d7c1
commit
93fd0b2536
@ -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
|
||||
|
@ -577,8 +577,11 @@ namespace skyline::kernel::svc {
|
||||
return;
|
||||
}
|
||||
|
||||
auto tmem{state.process->NewHandle<kernel::type::KTransferMemory>(address, size, permission)};
|
||||
state.process->memory.AddRef(tmem.item);
|
||||
auto tmem{state.process->NewHandle<kernel::type::KTransferMemory>(size)};
|
||||
if (!tmem.item->Map(span<u8>{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' : '-');
|
||||
|
||||
|
56
app/src/main/cpp/skyline/kernel/types/KMemory.cpp
Normal file
56
app/src/main/cpp/skyline/kernel/types/KMemory.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||
|
||||
#include <android/sharedmem.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#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<u8 *>(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<u8>{hostPtr, size};
|
||||
}
|
||||
|
||||
u8 *KMemory::Map(span<u8> 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<u8> 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);
|
||||
}
|
||||
}
|
@ -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 <u8> 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<u8> guest;
|
||||
span<u8> 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<u8> map, memory::Permission permission);
|
||||
|
||||
/**
|
||||
* @note 'ptr' needs to be in guest-reserved address space
|
||||
*/
|
||||
virtual void Unmap(span<u8> map);
|
||||
|
||||
virtual ~KMemory();
|
||||
};
|
||||
}
|
||||
|
@ -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 <android/sharedmem.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include "KSharedMemory.h"
|
||||
#include "KProcess.h"
|
||||
|
||||
namespace skyline::kernel::type {
|
||||
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size, KType type)
|
||||
: KMemory(state, type, span<u8>{}) {
|
||||
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<u8 *>(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<u8>{hostPtr, size};
|
||||
}
|
||||
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size)
|
||||
: KMemory(state, KType::KSharedMemory, size) {}
|
||||
|
||||
u8 *KSharedMemory::Map(span<u8> 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<u8 *>(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<u8>{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<u8> 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<u8>{};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -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<u8> 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
|
||||
|
64
app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
Normal file
64
app/src/main/cpp/skyline/kernel/types/KTransferMemory.cpp
Normal file
@ -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<u8> 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<u8> map) {
|
||||
KMemory::Unmap(map);
|
||||
|
||||
guest = span<u8>{};
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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<u8>{ptr, size}, permission);
|
||||
}
|
||||
KTransferMemory(const DeviceState &state, size_t size);
|
||||
|
||||
/**
|
||||
* @note 'ptr' needs to be in guest-reserved address space
|
||||
*/
|
||||
u8 *Map(span<u8> map, memory::Permission permission);
|
||||
|
||||
/**
|
||||
* @note 'ptr' needs to be in guest-reserved address space
|
||||
*/
|
||||
void Unmap(span<u8> map);
|
||||
|
||||
~KTransferMemory();
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user