Optimize Memory Implementation using Shared Memory

This commit further improves the memory implementation by using shared memory for all allocations so we won't have to depend on a kernel call for doing any host <-> guest memory transfers.
This commit is contained in:
◱ PixelyIon 2020-02-03 07:42:24 +05:30 committed by ◱ PixelyIon
parent d02267c34f
commit 03b65bd90a
10 changed files with 296 additions and 133 deletions

View File

@ -4,7 +4,7 @@
namespace skyline::kernel { namespace skyline::kernel {
ChunkDescriptor *MemoryManager::GetChunk(u64 address) { ChunkDescriptor *MemoryManager::GetChunk(u64 address) {
for (auto &chunk : chunkList) for (auto &chunk : chunkList)
if (chunk.address <= address && (chunk.address + chunk.size) >= address) if (chunk.address <= address && (chunk.address + chunk.size) > address)
return &chunk; return &chunk;
return nullptr; return nullptr;
} }
@ -13,7 +13,7 @@ namespace skyline::kernel {
auto chunk = GetChunk(address); auto chunk = GetChunk(address);
if (chunk) if (chunk)
for (auto &block : chunk->blockList) for (auto &block : chunk->blockList)
if (block.address <= address && (block.address + block.size) >= address) if (block.address <= address && (block.address + block.size) > address)
return &block; return &block;
return nullptr; return nullptr;
} }
@ -38,7 +38,7 @@ namespace skyline::kernel {
void MemoryManager::DeleteChunk(u64 address) { void MemoryManager::DeleteChunk(u64 address) {
chunkList.remove_if([address](const ChunkDescriptor &chunk) { chunkList.remove_if([address](const ChunkDescriptor &chunk) {
return chunk.address <= address && (chunk.address + chunk.size) >= address; return chunk.address <= address && (chunk.address + chunk.size) > address;
}); });
} }
@ -70,7 +70,7 @@ namespace skyline::kernel {
void MemoryManager::InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block) { void MemoryManager::InsertBlock(ChunkDescriptor *chunk, BlockDescriptor block) {
for (auto iter = chunk->blockList.begin(); iter != chunk->blockList.end(); iter++) { for (auto iter = chunk->blockList.begin(); iter != chunk->blockList.end(); iter++) {
if (iter->address <= block.address && (iter->address + iter->size) >= block.address) { if (iter->address <= block.address && (iter->address + iter->size) > block.address) {
if (iter->address == block.address && iter->size == block.size) { if (iter->address == block.address && iter->size == block.size) {
iter->attributes = block.attributes; iter->attributes = block.attributes;
iter->permission = block.permission; iter->permission = block.permission;
@ -133,7 +133,7 @@ namespace skyline::kernel {
auto chunk = GetChunk(address); auto chunk = GetChunk(address);
if (chunk) if (chunk)
for (auto &block : chunk->blockList) for (auto &block : chunk->blockList)
if (block.address <= address && (block.address + block.size) >= address) if (block.address <= address && (block.address + block.size) > address)
return DescriptorPack{block, *chunk}; return DescriptorPack{block, *chunk};
return std::nullopt; return std::nullopt;
} }

View File

@ -245,6 +245,7 @@ namespace skyline {
struct ChunkDescriptor { struct ChunkDescriptor {
u64 address; //!< The address of the current chunk u64 address; //!< The address of the current chunk
u64 size; //!< The size of the current chunk in bytes u64 size; //!< The size of the current chunk in bytes
u64 host; //!< The address of the chunk in the host
memory::MemoryState state; //!< The MemoryState for the current block memory::MemoryState state; //!< The MemoryState for the current block
std::forward_list<BlockDescriptor> blockList; //!< This linked list holds the block descriptors for all the children blocks of this Chunk std::forward_list<BlockDescriptor> blockList; //!< This linked list holds the block descriptors for all the children blocks of this Chunk
}; };

View File

@ -5,8 +5,8 @@ namespace skyline::kernel::svc {
void SetHeapSize(DeviceState &state) { void SetHeapSize(DeviceState &state) {
const u32 size = state.ctx->registers.w1; const u32 size = state.ctx->registers.w1;
if (size % constant::HeapSizeDiv != 0) { if (size % constant::HeapSizeDiv != 0) {
state.ctx->registers.x1 = 0;
state.ctx->registers.w0 = constant::status::InvSize; state.ctx->registers.w0 = constant::status::InvSize;
state.ctx->registers.x1 = 0;
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size); state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
return; return;
} }

View File

@ -1,22 +1,32 @@
#include "KPrivateMemory.h" #include "KPrivateMemory.h"
#include "KProcess.h" #include "KProcess.h"
#include <os.h> #include <os.h>
#include <android/sharedmem.h>
#include <asm/unistd.h> #include <asm/unistd.h>
#include <unistd.h>
namespace skyline::kernel::type { namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : address(address), size(size), KMemory(state, KType::KPrivateMemory) { KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : size(size), KMemory(state, KType::KPrivateMemory) {
Registers fregs{}; if (address && !utils::PageAligned(address))
fregs.x0 = address; throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address);
fregs.x1 = size; fd = ASharedMemory_create("KPrivateMemory", size);
fregs.x2 = static_cast<u64>(permission.Get()); if (fd < 0)
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0)); throw exception("An error occurred while creating shared memory: {}", fd);
fregs.x4 = static_cast<u64>(-1); auto host = mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
fregs.x8 = __NR_mmap; if (host == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
Registers fregs{
.x0 = address,
.x1 = size,
.x2 = static_cast<u64>(permission.Get()),
.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0)),
.x4 = static_cast<u64>(fd),
.x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping private section in child process"); throw exception("An error occurred while mapping private memory in child process");
if (!this->address) this->address = fregs.x0;
this->address = fregs.x0;
BlockDescriptor block{ BlockDescriptor block{
.address = fregs.x0, .address = fregs.x0,
.size = size, .size = size,
@ -25,6 +35,7 @@ namespace skyline::kernel::type {
ChunkDescriptor chunk{ ChunkDescriptor chunk{
.address = fregs.x0, .address = fregs.x0,
.size = size, .size = size,
.host = reinterpret_cast<u64>(host),
.state = memState, .state = memState,
.blockList = {block}, .blockList = {block},
}; };
@ -32,33 +43,70 @@ namespace skyline::kernel::type {
} }
void KPrivateMemory::Resize(size_t nSize) { void KPrivateMemory::Resize(size_t nSize) {
Registers fregs{}; if (close(fd) < 0)
fregs.x0 = address; throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fregs.x1 = size; fd = ASharedMemory_create("KPrivateMemory", nSize);
fregs.x2 = nSize; if (fd < 0)
fregs.x8 = __NR_mremap; throw exception("An error occurred while creating shared memory: {}", fd);
Registers fregs{
.x0 = address,
.x1 = size,
.x8 = __NR_munmap
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while remapping private section in child process"); throw exception("An error occurred while unmapping private memory in child process");
size = nSize; fregs = {
.x0 = address,
.x1 = nSize,
.x2 = static_cast<u64>(PROT_READ | PROT_WRITE | PROT_EXEC),
.x3 = static_cast<u64>(MAP_SHARED | MAP_FIXED),
.x4 = static_cast<u64>(fd),
.x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
throw exception("An error occurred while remapping private memory in child process");
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
MemoryManager::ResizeChunk(chunk, size); state.process->WriteMemory(reinterpret_cast<void *>(chunk->host), address, std::min(nSize, size), true);
for (const auto &block : chunk->blockList) {
if((block.address - chunk->address) < size) {
fregs = {
.x0 = block.address,
.x1 = std::min(block.size, (chunk->address + nSize) - block.address),
.x2 = static_cast<u64>(block.permission.Get()),
.x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
throw exception("An error occurred while updating private memory's permissions in child process");
} else
break;
}
munmap(reinterpret_cast<void *>(chunk->host), size);
auto host = mmap(reinterpret_cast<void *>(chunk->host), nSize, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
if (host == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
chunk->host = reinterpret_cast<u64>(host);
MemoryManager::ResizeChunk(chunk, nSize);
size = nSize;
} }
void KPrivateMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) { void KPrivateMemory::UpdatePermission(const u64 address, const u64 size, memory::Permission permission) {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x2 = static_cast<u64>(permission.Get()); .x2 = static_cast<u64>(permission.Get()),
fregs.x8 = __NR_mprotect; .x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating private section's permissions in child process"); throw exception("An error occurred while updating private memory's permissions in child process");
auto chunk = state.os->memory.GetChunk(address); auto chunk = state.os->memory.GetChunk(address);
BlockDescriptor block{ BlockDescriptor block{
.address = address, .address = address,
.size = size, .size = size,
.permission = permission, .permission = permission,
}; };
MemoryManager::InsertBlock(chunk, block); MemoryManager::InsertBlock(chunk, block);
} }
@ -66,14 +114,17 @@ namespace skyline::kernel::type {
KPrivateMemory::~KPrivateMemory() { KPrivateMemory::~KPrivateMemory() {
try { try {
if (state.process) { if (state.process) {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x8 = __NR_munmap; .x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
} }
} catch (const std::exception &) { } catch (const std::exception &) {
} }
auto chunk = state.os->memory.GetChunk(address);
munmap(reinterpret_cast<void *>(chunk->host), chunk->size);
state.os->memory.DeleteChunk(address); state.os->memory.DeleteChunk(address);
} }
}; };

View File

@ -7,6 +7,9 @@ namespace skyline::kernel::type {
* @brief KPrivateMemory is used to map memory local to the guest process * @brief KPrivateMemory is used to map memory local to the guest process
*/ */
class KPrivateMemory : public KMemory { class KPrivateMemory : public KMemory {
private:
int fd; //!< A file descriptor to the underlying shared memory
public: public:
u64 address{}; //!< The address of the allocated memory u64 address{}; //!< The address of the allocated memory
size_t size{}; //!< The size of the allocated memory size_t size{}; //!< The size of the allocated memory

View File

@ -83,45 +83,68 @@ namespace skyline::kernel::type {
return process; return process;
} }
void KProcess::ReadMemory(void *destination, u64 offset, size_t size) const { u64 KProcess::GetHostAddress(u64 address) const {
auto chunk = state.os->memory.GetChunk(address);
return (chunk && chunk->host) ? chunk->host + (address - chunk->address) : 0;
}
void KProcess::ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest) const {
if (!forceGuest) {
auto source = GetHostAddress(offset);
if (source) {
memcpy(destination, reinterpret_cast<void *>(source), size);
return;
}
}
struct iovec local{ struct iovec local{
.iov_base = destination, .iov_base = destination,
.iov_len = size .iov_len = size,
}; };
struct iovec remote{ struct iovec remote{
.iov_base = reinterpret_cast<void *>(offset), .iov_base = reinterpret_cast<void *>(offset),
.iov_len = size .iov_len = size,
}; };
if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0) if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0)
pread64(memFd, destination, size, offset); pread64(memFd, destination, size, offset);
} }
void KProcess::WriteMemory(void *source, u64 offset, size_t size) const { void KProcess::WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest) const {
if(!forceGuest) {
auto destination = GetHostAddress(offset);
if (destination) {
memcpy(reinterpret_cast<void *>(destination), source, size);
return;
}
}
struct iovec local{ struct iovec local{
.iov_base = source, .iov_base = source,
.iov_len = size .iov_len = size,
}; };
struct iovec remote{ struct iovec remote{
.iov_base = reinterpret_cast<void *>(offset), .iov_base = reinterpret_cast<void *>(offset),
.iov_len = size .iov_len = size,
}; };
if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0) if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0)
pwrite64(memFd, source, size, offset); pwrite64(memFd, source, size, offset);
} }
void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const { void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const {
if (size <= PAGE_SIZE) { auto sourceHost = GetHostAddress(source);
std::vector<u8> buffer(size); auto destinationHost = GetHostAddress(destination);
state.process->ReadMemory(buffer.data(), source, size); if(sourceHost && destinationHost) {
state.process->WriteMemory(buffer.data(), destination, size); memcpy(reinterpret_cast<void *>(destinationHost), reinterpret_cast<const void *>(sourceHost), size);
} else { } else {
Registers fregs{}; if (size <= PAGE_SIZE) {
fregs.x0 = source; std::vector<u8> buffer(size);
fregs.x1 = destination; state.process->ReadMemory(buffer.data(), source, size);
fregs.x2 = size; state.process->WriteMemory(buffer.data(), destination, size);
state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs); } else {
Registers fregs{};
fregs.x0 = source;
fregs.x1 = destination;
fregs.x2 = size;
state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs);
}
} }
} }
@ -145,8 +168,8 @@ namespace skyline::kernel::type {
void KProcess::MutexLock(u64 address, handle_t owner, bool alwaysLock) { void KProcess::MutexLock(u64 address, handle_t owner, bool alwaysLock) {
std::unique_lock lock(mutexLock); std::unique_lock lock(mutexLock);
u32 mtxVal = ReadMemory<u32>(address); u32 mtxVal = ReadMemory<u32>(address);
if(alwaysLock) { if (alwaysLock) {
if(!mtxVal) { if (!mtxVal) {
state.logger->Warn("Mutex Value was 0"); state.logger->Warn("Mutex Value was 0");
mtxVal = (constant::MtxOwnerMask & state.thread->handle); mtxVal = (constant::MtxOwnerMask & state.thread->handle);
WriteMemory<u32>(mtxVal, address); WriteMemory<u32>(mtxVal, address);
@ -159,7 +182,7 @@ namespace skyline::kernel::type {
} }
auto &mtxWaiters = mutexes[address]; auto &mtxWaiters = mutexes[address];
std::shared_ptr<WaitStatus> status; std::shared_ptr<WaitStatus> status;
for (auto it = mtxWaiters.begin();;++it) { for (auto it = mtxWaiters.begin();; ++it) {
if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority) if (it != mtxWaiters.end() && (*it)->priority >= state.thread->priority)
continue; continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid); status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid);
@ -170,7 +193,7 @@ namespace skyline::kernel::type {
while (!status->flag); while (!status->flag);
lock.lock(); lock.lock();
for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it) for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it)
if((*it)->pid == state.thread->pid) { if ((*it)->pid == state.thread->pid) {
mtxWaiters.erase(it); mtxWaiters.erase(it);
break; break;
} }
@ -197,7 +220,7 @@ namespace skyline::kernel::type {
std::unique_lock lock(conditionalLock); std::unique_lock lock(conditionalLock);
auto &condWaiters = conditionals[address]; auto &condWaiters = conditionals[address];
std::shared_ptr<WaitStatus> status; std::shared_ptr<WaitStatus> status;
for (auto it = condWaiters.begin();;++it) { for (auto it = condWaiters.begin();; ++it) {
if (it != condWaiters.end() && (*it)->priority >= state.thread->priority) if (it != condWaiters.end() && (*it)->priority >= state.thread->priority)
continue; continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid); status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid);
@ -213,7 +236,7 @@ namespace skyline::kernel::type {
} }
lock.lock(); lock.lock();
for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it) for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it)
if((*it)->pid == state.thread->pid) { if ((*it)->pid == state.thread->pid) {
condWaiters.erase(it); condWaiters.erase(it);
break; break;
} }

View File

@ -169,21 +169,41 @@ namespace skyline::kernel::type {
WriteMemory(&item, address, sizeof(Type)); WriteMemory(&item, address, sizeof(Type));
} }
/**
* @brief This returns the host address for a specific address in guest memory
* @param address The corresponding guest address
* @return The corresponding host address
*/
u64 GetHostAddress(const u64 address) const;
/**
* @tparam Type The type of the pointer to return
* @param address The address on the guest
* @return A pointer corresponding to a certain address on the guest
* @note This can return a nullptr if the address is invalid
*/
template<typename Type>
inline Type* GetPointer(const u64 address) const {
return reinterpret_cast<Type*>(GetHostAddress(address));
}
/** /**
* @brief Read data from the process's memory * @brief Read data from the process's memory
* @param destination The address to the location where the process memory is written * @param destination The address to the location where the process memory is written
* @param offset The address to read from in process memory * @param offset The address to read from in process memory
* @param size The amount of memory to be read * @param size The amount of memory to be read
* @param forceGuest This flag forces the write to be performed in guest address space
*/ */
void ReadMemory(void *destination, u64 offset, size_t size) const; void ReadMemory(void *destination, const u64 offset, const size_t size, const bool forceGuest = false) const;
/** /**
* @brief Write to the process's memory * @brief Write to the process's memory
* @param source The address of where the data to be written is present * @param source The address of where the data to be written is present
* @param offset The address to write to in process memory * @param offset The address to write to in process memory
* @param size The amount of memory to be written * @param size The amount of memory to be written
* @param forceGuest This flag forces the write to be performed in guest address space
*/ */
void WriteMemory(void *source, u64 offset, size_t size) const; void WriteMemory(void *source, const u64 offset, const size_t size, const bool forceGuest = false) const;
/** /**
* @brief Copy one chunk to another in the process's memory * @brief Copy one chunk to another in the process's memory
@ -191,7 +211,7 @@ namespace skyline::kernel::type {
* @param destination The address to write the read data to * @param destination The address to write the read data to
* @param size The amount of memory to be copied * @param size The amount of memory to be copied
*/ */
void CopyMemory(u64 source, u64 destination, size_t size) const; void CopyMemory(const u64 source, const u64 destination, const size_t size) const;
/** /**
* @brief Creates a new handle to a KObject and adds it to the process handle_table * @brief Creates a new handle to a KObject and adds it to the process handle_table

View File

@ -7,7 +7,9 @@
namespace skyline::kernel::type { namespace skyline::kernel::type {
KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : initialState(memState), KMemory(state, KType::KSharedMemory) { KSharedMemory::KSharedMemory(const DeviceState &state, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : initialState(memState), KMemory(state, KType::KSharedMemory) {
fd = ASharedMemory_create("", size); if (address && !utils::PageAligned(address))
throw exception("KSharedMemory was created with non-page-aligned address: 0x{:X}", address);
fd = ASharedMemory_create("KSharedMemory", size);
if (fd < 0) if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd); throw exception("An error occurred while creating shared memory: {}", fd);
address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0), fd, 0)); address = reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | ((address) ? MAP_FIXED : 0), fd, 0));
@ -17,19 +19,23 @@ namespace skyline::kernel::type {
} }
u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) { u64 KSharedMemory::Map(const u64 address, const u64 size, memory::Permission permission) {
Registers fregs{}; if (address && !utils::PageAligned(address))
fregs.x0 = address; throw exception("KSharedMemory was mapped to a non-page-aligned address: 0x{:X}", address);
fregs.x1 = size; Registers fregs{
fregs.x2 = static_cast<u64>(permission.Get()); .x0 = address,
fregs.x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0)); .x1 = size,
fregs.x4 = static_cast<u64>(fd); .x2 = static_cast<u64>(permission.Get()),
fregs.x8 = __NR_mmap; .x3 = static_cast<u64>(MAP_SHARED | ((address) ? MAP_FIXED : 0)),
.x4 = static_cast<u64>(fd),
.x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping shared memory in guest"); throw exception("An error occurred while mapping shared memory in guest");
guest = {.address = fregs.x0, .size = size, .permission = permission}; guest = {.address = fregs.x0, .size = size, .permission = permission};
ChunkDescriptor chunk{ ChunkDescriptor chunk{
.address = fregs.x0, .address = fregs.x0,
.host = kernel.address,
.size = size, .size = size,
.state = initialState, .state = initialState,
}; };
@ -44,33 +50,81 @@ namespace skyline::kernel::type {
} }
void KSharedMemory::Resize(size_t size) { void KSharedMemory::Resize(size_t size) {
if (guest.valid()) { if (guest.valid() && kernel.valid()) {
Registers fregs{}; if (close(fd) < 0)
fregs.x0 = guest.address; throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fregs.x1 = guest.size; fd = ASharedMemory_create("KSharedMemory", size);
fregs.x2 = size; if (fd < 0)
fregs.x8 = __NR_mremap; throw exception("An error occurred while creating shared memory: {}", fd);
Registers fregs{
.x0 = guest.address,
.x1 = guest.size,
.x8 = __NR_munmap
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while remapping shared memory in guest"); throw exception("An error occurred while unmapping private memory in child process");
guest.size = size; fregs = {
.x0 = guest.address,
.x1 = size,
.x2 = static_cast<u64>(PROT_READ | PROT_WRITE | PROT_EXEC),
.x3 = static_cast<u64>(MAP_SHARED | MAP_FIXED),
.x4 = static_cast<u64>(fd),
.x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
throw exception("An error occurred while remapping private memory in child process");
state.process->WriteMemory(reinterpret_cast<void *>(kernel.address), guest.address, std::min(guest.size, size), true);
auto chunk = state.os->memory.GetChunk(guest.address); auto chunk = state.os->memory.GetChunk(guest.address);
for (const auto &block : chunk->blockList) {
if((block.address - chunk->address) < guest.size) {
fregs = {
.x0 = block.address,
.x1 = std::min(block.size, (chunk->address + size) - block.address),
.x2 = static_cast<u64>(block.permission.Get()),
.x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
throw exception("An error occurred while updating private memory's permissions in child process");
} else
break;
}
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
auto host = mmap(reinterpret_cast<void *>(chunk->host), size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd, 0);
if (host == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
guest.size = size;
MemoryManager::ResizeChunk(chunk, size); MemoryManager::ResizeChunk(chunk, size);
} } else if (kernel.valid()) {
if (kernel.valid()) { if (close(fd) < 0)
if (mremap(reinterpret_cast<void *>(kernel.address), kernel.size, size, 0) == MAP_FAILED) throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
throw exception("An error occurred while remapping shared region: {}", strerror(errno)); fd = ASharedMemory_create("KSharedMemory", size);
if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd);
std::vector<u8> data(std::min(size, kernel.size));
memcpy(data.data(), reinterpret_cast<const void *>(kernel.address), std::min(size, kernel.size));
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
auto address = mmap(reinterpret_cast<void *>(kernel.address), size, kernel.permission.Get(), MAP_SHARED, fd, 0);
if (address == MAP_FAILED)
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
memcpy(address, data.data(), std::min(size, kernel.size));
kernel.address = reinterpret_cast<u64>(address);
kernel.size = size; kernel.size = size;
} else {
throw exception("Cannot resize KSharedMemory that's only on guest");
} }
} }
void KSharedMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host) { void KSharedMemory::UpdatePermission(u64 address, u64 size, memory::Permission permission, bool host) {
if (guest.valid() && !host) { if (guest.valid() && !host) {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x2 = static_cast<u64>(permission.Get()); .x2 = static_cast<u64>(permission.Get()),
fregs.x8 = __NR_mprotect; .x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating shared memory's permissions in guest"); throw exception("An error occurred while updating shared memory's permissions in guest");
@ -92,16 +146,17 @@ namespace skyline::kernel::type {
KSharedMemory::~KSharedMemory() { KSharedMemory::~KSharedMemory() {
try { try {
if (guest.valid() && state.process) { if (guest.valid() && state.process) {
Registers fregs{}; Registers fregs{
fregs.x0 = guest.address; .x0 = guest.address,
fregs.x1 = guest.size; .x1 = guest.size,
fregs.x8 = __NR_munmap; .x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
} }
if (kernel.valid())
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
} catch (const std::exception &) { } catch (const std::exception &) {
} }
if (kernel.valid())
munmap(reinterpret_cast<void *>(kernel.address), kernel.size);
state.os->memory.DeleteChunk(guest.address); state.os->memory.DeleteChunk(guest.address);
close(fd); close(fd);
} }

View File

@ -70,7 +70,6 @@ namespace skyline::kernel::type {
UpdatePermission(address, size, permission, false); UpdatePermission(address, size, permission, false);
} }
/** /**
* @brief Updates the permissions of a chunk of mapped memory * @brief Updates the permissions of a chunk of mapped memory
* @param permission The new permissions to be set for the memory * @param permission The new permissions to be set for the memory

View File

@ -5,6 +5,8 @@
namespace skyline::kernel::type { namespace skyline::kernel::type {
KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) { KTransferMemory::KTransferMemory(const DeviceState &state, bool host, u64 address, size_t size, const memory::Permission permission, memory::MemoryState memState) : host(host), size(size), KMemory(state, KType::KTransferMemory) {
if (address && !utils::PageAligned(address))
throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address);
BlockDescriptor block{ BlockDescriptor block{
.size = size, .size = size,
.permission = permission, .permission = permission,
@ -23,13 +25,14 @@ namespace skyline::kernel::type {
chunk.blockList.front().address = address; chunk.blockList.front().address = address;
hostChunk = chunk; hostChunk = chunk;
} else { } else {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x2 = static_cast<u64 >(permission.Get()); .x2 = static_cast<u64 >(permission.Get()),
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)); .x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)),
fregs.x4 = static_cast<u64>(-1); .x4 = static_cast<u64>(-1),
fregs.x8 = __NR_mmap; .x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping shared region in child process"); throw exception("An error occurred while mapping shared region in child process");
@ -41,6 +44,8 @@ namespace skyline::kernel::type {
} }
u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) { u64 KTransferMemory::Transfer(bool mHost, u64 nAddress, u64 nSize) {
if (nAddress && !utils::PageAligned(nAddress))
throw exception("KTransferMemory was transferred to a non-page-aligned address: 0x{:X}", nAddress);
nSize = nSize ? nSize : size; nSize = nSize ? nSize : size;
ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address); ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address);
chunk.address = nAddress; chunk.address = nAddress;
@ -49,13 +54,14 @@ namespace skyline::kernel::type {
for (auto &block : chunk.blockList) { for (auto &block : chunk.blockList) {
block.address = nAddress + (block.address - address); block.address = nAddress + (block.address - address);
if ((mHost && !host) || (!mHost && !host)) { if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{}; Registers fregs{
fregs.x0 = block.address; .x0 = block.address,
fregs.x1 = block.size; .x1 = block.size,
fregs.x2 = (block.permission.w) ? static_cast<u64>(block.permission.Get()) : (PROT_READ | PROT_WRITE); .x2 = (block.permission.w) ? static_cast<u64>(block.permission.Get()) : (PROT_READ | PROT_WRITE),
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0)); .x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0)),
fregs.x4 = static_cast<u64>(-1); .x4 = static_cast<u64>(-1),
fregs.x8 = __NR_mmap; .x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while mapping transfer memory in child process"); throw exception("An error occurred while mapping transfer memory in child process");
@ -80,11 +86,12 @@ namespace skyline::kernel::type {
if (mprotect(reinterpret_cast<void *>(block.address), block.size, block.permission.Get()) == reinterpret_cast<u64>(MAP_FAILED)) if (mprotect(reinterpret_cast<void *>(block.address), block.size, block.permission.Get()) == reinterpret_cast<u64>(MAP_FAILED))
throw exception("An error occurred while remapping transfer memory: {}", strerror(errno)); throw exception("An error occurred while remapping transfer memory: {}", strerror(errno));
} else { } else {
Registers fregs{}; Registers fregs{
fregs.x0 = block.address; .x0 = block.address,
fregs.x1 = block.size; .x1 = block.size,
fregs.x2 = static_cast<u64>(block.permission.Get()); .x2 = static_cast<u64>(block.permission.Get()),
fregs.x8 = __NR_mprotect; .x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating transfer memory's permissions in guest"); throw exception("An error occurred while updating transfer memory's permissions in guest");
@ -103,10 +110,11 @@ namespace skyline::kernel::type {
state.os->memory.InsertChunk(chunk); state.os->memory.InsertChunk(chunk);
} }
if ((mHost && !host) || (!mHost && !host)) { if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x8 = __NR_munmap; .x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while unmapping transfer memory in child process"); throw exception("An error occurred while unmapping transfer memory in child process");
@ -125,11 +133,12 @@ namespace skyline::kernel::type {
if (mremap(reinterpret_cast<void *>(address), size, nSize, 0) == MAP_FAILED) if (mremap(reinterpret_cast<void *>(address), size, nSize, 0) == MAP_FAILED)
throw exception("An error occurred while remapping transfer memory in host: {}", strerror(errno)); throw exception("An error occurred while remapping transfer memory in host: {}", strerror(errno));
} else { } else {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x2 = nSize; .x2 = nSize,
fregs.x8 = __NR_mremap; .x8 = __NR_mremap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while remapping transfer memory in guest"); throw exception("An error occurred while remapping transfer memory in guest");
@ -150,11 +159,12 @@ namespace skyline::kernel::type {
throw exception("An occurred while remapping transfer memory: {}", strerror(errno)); throw exception("An occurred while remapping transfer memory: {}", strerror(errno));
MemoryManager::InsertBlock(&hostChunk, block); MemoryManager::InsertBlock(&hostChunk, block);
} else { } else {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x2 = static_cast<u64>(permission.Get()); .x2 = static_cast<u64>(permission.Get()),
fregs.x8 = __NR_mprotect; .x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0) if (fregs.x0 < 0)
throw exception("An error occurred while updating transfer memory's permissions in guest"); throw exception("An error occurred while updating transfer memory's permissions in guest");
@ -168,10 +178,11 @@ namespace skyline::kernel::type {
munmap(reinterpret_cast<void *>(address), size); munmap(reinterpret_cast<void *>(address), size);
else if (state.process) { else if (state.process) {
try { try {
Registers fregs{}; Registers fregs{
fregs.x0 = address; .x0 = address,
fregs.x1 = size; .x1 = size,
fregs.x8 = __NR_munmap; .x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs); state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
state.os->memory.DeleteChunk(address); state.os->memory.DeleteChunk(address);
} catch (const std::exception &) { } catch (const std::exception &) {