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 {
ChunkDescriptor *MemoryManager::GetChunk(u64 address) {
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 nullptr;
}
@ -13,7 +13,7 @@ namespace skyline::kernel {
auto chunk = GetChunk(address);
if (chunk)
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 nullptr;
}
@ -38,7 +38,7 @@ namespace skyline::kernel {
void MemoryManager::DeleteChunk(u64 address) {
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) {
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) {
iter->attributes = block.attributes;
iter->permission = block.permission;
@ -133,7 +133,7 @@ namespace skyline::kernel {
auto chunk = GetChunk(address);
if (chunk)
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 std::nullopt;
}

View File

@ -245,6 +245,7 @@ namespace skyline {
struct ChunkDescriptor {
u64 address; //!< The address of the current chunk
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
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) {
const u32 size = state.ctx->registers.w1;
if (size % constant::HeapSizeDiv != 0) {
state.ctx->registers.x1 = 0;
state.ctx->registers.w0 = constant::status::InvSize;
state.ctx->registers.x1 = 0;
state.logger->Warn("svcSetHeapSize: 'size' not divisible by 2MB: {}", size);
return;
}

View File

@ -1,22 +1,32 @@
#include "KPrivateMemory.h"
#include "KProcess.h"
#include <os.h>
#include <android/sharedmem.h>
#include <asm/unistd.h>
#include <unistd.h>
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) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64>(permission.Get());
fregs.x3 = static_cast<u64>(MAP_PRIVATE | MAP_ANONYMOUS | ((address) ? MAP_FIXED : 0));
fregs.x4 = static_cast<u64>(-1);
fregs.x8 = __NR_mmap;
KPrivateMemory::KPrivateMemory(const DeviceState &state, u64 address, size_t size, memory::Permission permission, const memory::MemoryState memState) : size(size), KMemory(state, KType::KPrivateMemory) {
if (address && !utils::PageAligned(address))
throw exception("KPrivateMemory was created with non-page-aligned address: 0x{:X}", address);
fd = ASharedMemory_create("KPrivateMemory", size);
if (fd < 0)
throw exception("An error occurred while creating shared memory: {}", fd);
auto host = mmap(nullptr, 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));
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);
if (fregs.x0 < 0)
throw exception("An error occurred while mapping private section in child process");
if (!this->address)
this->address = fregs.x0;
throw exception("An error occurred while mapping private memory in child process");
this->address = fregs.x0;
BlockDescriptor block{
.address = fregs.x0,
.size = size,
@ -25,6 +35,7 @@ namespace skyline::kernel::type {
ChunkDescriptor chunk{
.address = fregs.x0,
.size = size,
.host = reinterpret_cast<u64>(host),
.state = memState,
.blockList = {block},
};
@ -32,33 +43,70 @@ namespace skyline::kernel::type {
}
void KPrivateMemory::Resize(size_t nSize) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = nSize;
fregs.x8 = __NR_mremap;
if (close(fd) < 0)
throw exception("An error occurred while trying to close shared memory FD: {}", strerror(errno));
fd = ASharedMemory_create("KPrivateMemory", nSize);
if (fd < 0)
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);
if (fregs.x0 < 0)
throw exception("An error occurred while remapping private section in child process");
size = nSize;
throw exception("An error occurred while unmapping private memory in child process");
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);
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) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64>(permission.Get());
fregs.x8 = __NR_mprotect;
Registers fregs{
.x0 = address,
.x1 = size,
.x2 = static_cast<u64>(permission.Get()),
.x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
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);
BlockDescriptor block{
.address = address,
.size = size,
.permission = permission,
.address = address,
.size = size,
.permission = permission,
};
MemoryManager::InsertBlock(chunk, block);
}
@ -66,14 +114,17 @@ namespace skyline::kernel::type {
KPrivateMemory::~KPrivateMemory() {
try {
if (state.process) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
Registers fregs{
.x0 = address,
.x1 = size,
.x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
}
} catch (const std::exception &) {
}
auto chunk = state.os->memory.GetChunk(address);
munmap(reinterpret_cast<void *>(chunk->host), chunk->size);
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
*/
class KPrivateMemory : public KMemory {
private:
int fd; //!< A file descriptor to the underlying shared memory
public:
u64 address{}; //!< The address 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;
}
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{
.iov_base = destination,
.iov_len = size
.iov_len = size,
};
struct iovec remote{
.iov_base = reinterpret_cast<void *>(offset),
.iov_len = size
.iov_len = size,
};
if (process_vm_readv(pid, &local, 1, &remote, 1, 0) < 0)
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{
.iov_base = source,
.iov_len = size
.iov_len = size,
};
struct iovec remote{
.iov_base = reinterpret_cast<void *>(offset),
.iov_len = size
.iov_len = size,
};
if (process_vm_writev(pid, &local, 1, &remote, 1, 0) < 0)
pwrite64(memFd, source, size, offset);
}
void KProcess::CopyMemory(u64 source, u64 destination, size_t size) const {
if (size <= PAGE_SIZE) {
std::vector<u8> buffer(size);
state.process->ReadMemory(buffer.data(), source, size);
state.process->WriteMemory(buffer.data(), destination, size);
auto sourceHost = GetHostAddress(source);
auto destinationHost = GetHostAddress(destination);
if(sourceHost && destinationHost) {
memcpy(reinterpret_cast<void *>(destinationHost), reinterpret_cast<const void *>(sourceHost), size);
} else {
Registers fregs{};
fregs.x0 = source;
fregs.x1 = destination;
fregs.x2 = size;
state.nce->ExecuteFunction(ThreadCall::Memcopy, fregs);
if (size <= PAGE_SIZE) {
std::vector<u8> buffer(size);
state.process->ReadMemory(buffer.data(), source, size);
state.process->WriteMemory(buffer.data(), destination, size);
} 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) {
std::unique_lock lock(mutexLock);
u32 mtxVal = ReadMemory<u32>(address);
if(alwaysLock) {
if(!mtxVal) {
if (alwaysLock) {
if (!mtxVal) {
state.logger->Warn("Mutex Value was 0");
mtxVal = (constant::MtxOwnerMask & state.thread->handle);
WriteMemory<u32>(mtxVal, address);
@ -159,7 +182,7 @@ namespace skyline::kernel::type {
}
auto &mtxWaiters = mutexes[address];
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)
continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid);
@ -170,7 +193,7 @@ namespace skyline::kernel::type {
while (!status->flag);
lock.lock();
for (auto it = mtxWaiters.begin(); it != mtxWaiters.end(); ++it)
if((*it)->pid == state.thread->pid) {
if ((*it)->pid == state.thread->pid) {
mtxWaiters.erase(it);
break;
}
@ -197,7 +220,7 @@ namespace skyline::kernel::type {
std::unique_lock lock(conditionalLock);
auto &condWaiters = conditionals[address];
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)
continue;
status = std::make_shared<WaitStatus>(state.thread->priority, state.thread->pid);
@ -213,7 +236,7 @@ namespace skyline::kernel::type {
}
lock.lock();
for (auto it = condWaiters.begin(); it != condWaiters.end(); ++it)
if((*it)->pid == state.thread->pid) {
if ((*it)->pid == state.thread->pid) {
condWaiters.erase(it);
break;
}

View File

@ -169,21 +169,41 @@ namespace skyline::kernel::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
* @param destination The address to the location where the process memory is written
* @param offset The address to read from in process memory
* @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
* @param source The address of where the data to be written is present
* @param offset The address to write to in process memory
* @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
@ -191,7 +211,7 @@ namespace skyline::kernel::type {
* @param destination The address to write the read data to
* @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

View File

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

View File

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

View File

@ -5,6 +5,8 @@
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) {
if (address && !utils::PageAligned(address))
throw exception("KTransferMemory was created with non-page-aligned address: 0x{:X}", address);
BlockDescriptor block{
.size = size,
.permission = permission,
@ -23,13 +25,14 @@ namespace skyline::kernel::type {
chunk.blockList.front().address = address;
hostChunk = chunk;
} else {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64 >(permission.Get());
fregs.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0));
fregs.x4 = static_cast<u64>(-1);
fregs.x8 = __NR_mmap;
Registers fregs{
.x0 = address,
.x1 = size,
.x2 = static_cast<u64 >(permission.Get()),
.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((address) ? MAP_FIXED : 0)),
.x4 = static_cast<u64>(-1),
.x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
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) {
if (nAddress && !utils::PageAligned(nAddress))
throw exception("KTransferMemory was transferred to a non-page-aligned address: 0x{:X}", nAddress);
nSize = nSize ? nSize : size;
ChunkDescriptor chunk = host ? hostChunk : *state.os->memory.GetChunk(address);
chunk.address = nAddress;
@ -49,13 +54,14 @@ namespace skyline::kernel::type {
for (auto &block : chunk.blockList) {
block.address = nAddress + (block.address - address);
if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{};
fregs.x0 = block.address;
fregs.x1 = block.size;
fregs.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));
fregs.x4 = static_cast<u64>(-1);
fregs.x8 = __NR_mmap;
Registers fregs{
.x0 = block.address,
.x1 = block.size,
.x2 = (block.permission.w) ? static_cast<u64>(block.permission.Get()) : (PROT_READ | PROT_WRITE),
.x3 = static_cast<u64>(MAP_ANONYMOUS | MAP_PRIVATE | ((nAddress) ? MAP_FIXED : 0)),
.x4 = static_cast<u64>(-1),
.x8 = __NR_mmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
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))
throw exception("An error occurred while remapping transfer memory: {}", strerror(errno));
} else {
Registers fregs{};
fregs.x0 = block.address;
fregs.x1 = block.size;
fregs.x2 = static_cast<u64>(block.permission.Get());
fregs.x8 = __NR_mprotect;
Registers fregs{
.x0 = block.address,
.x1 = block.size,
.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 transfer memory's permissions in guest");
@ -103,10 +110,11 @@ namespace skyline::kernel::type {
state.os->memory.InsertChunk(chunk);
}
if ((mHost && !host) || (!mHost && !host)) {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
Registers fregs{
.x0 = address,
.x1 = size,
.x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
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)
throw exception("An error occurred while remapping transfer memory in host: {}", strerror(errno));
} else {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = nSize;
fregs.x8 = __NR_mremap;
Registers fregs{
.x0 = address,
.x1 = size,
.x2 = nSize,
.x8 = __NR_mremap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
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));
MemoryManager::InsertBlock(&hostChunk, block);
} else {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x2 = static_cast<u64>(permission.Get());
fregs.x8 = __NR_mprotect;
Registers fregs{
.x0 = address,
.x1 = size,
.x2 = static_cast<u64>(permission.Get()),
.x8 = __NR_mprotect,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
if (fregs.x0 < 0)
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);
else if (state.process) {
try {
Registers fregs{};
fregs.x0 = address;
fregs.x1 = size;
fregs.x8 = __NR_munmap;
Registers fregs{
.x0 = address,
.x1 = size,
.x8 = __NR_munmap,
};
state.nce->ExecuteFunction(ThreadCall::Syscall, fregs);
state.os->memory.DeleteChunk(address);
} catch (const std::exception &) {