skyline/app/src/main/cpp/switch/kernel/types/KSharedMemory.cpp
◱ PixelyIon a54f5ff578 Milestone 2 - Memory & IPC Marshalling
This commit introduces a new memory model that supports true shared memory with separate permissions for remote and local processes, implements svcQueryMemory and completes svcGetInfo further, adds IPC support with the IpcRequest and IpcResponse classes.
2019-09-25 02:28:25 +05:30

120 lines
5.7 KiB
C++

#include "KSharedMemory.h"
#include "../../nce.h"
#include "../../os.h"
#include <android/sharedmem.h>
#include <fcntl.h>
#include <unistd.h>
namespace lightSwitch::kernel::type {
u64 MapFunc(u64 address, size_t size, u64 perms, u64 fd) {
return reinterpret_cast<u64>(mmap(reinterpret_cast<void *>(address), size, static_cast<int>(perms), MAP_SHARED | ((address) ? MAP_FIXED : 0), static_cast<int>(fd), 0)); // NOLINT(hicpp-signed-bitwise)
}
KSharedMemory::KSharedMemory(const device_state &state, size_t size, const Memory::Permission local_permission, const Memory::Permission remote_permission, Memory::Type type, handle_t handle, pid_t owner_pid) : state(state), size(size), local_permission(local_permission), remote_permission(remote_permission), type(type), owner_pid(owner_pid), KObject(handle, KObjectType::KSharedMemory) {
fd = ASharedMemory_create("", size);
}
void KSharedMemory::Map(u64 address) {
this->address = address;
for (auto process : state.os->process_vec) {
user_pt_regs fregs = {0};
fregs.regs[0] = this->address;
fregs.regs[1] = size;
if (process == owner_pid)
fregs.regs[2] = static_cast<u64 >(local_permission.get());
else
fregs.regs[2] = static_cast<u64>(remote_permission.get());
fregs.regs[3] = static_cast<u64>(fd);
state.nce->ExecuteFunction(reinterpret_cast<void *>(MapFunc), fregs, process);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
throw exception("An error occurred while mapping shared region in child process");
if (!this->address) this->address = fregs.regs[0];
}
this->address = MapFunc(this->address, size, static_cast<u64>(owner_pid ? remote_permission.get() : local_permission.get()), static_cast<u64>(fd));
if (this->address == reinterpret_cast<u64>(MAP_FAILED)) // NOLINT(hicpp-signed-bitwise)
throw exception(fmt::format("An occurred while mapping shared region: {}", strerror(errno)));
}
u64 UnmapFunc(u64 address, size_t size) {
return static_cast<u64>(munmap(reinterpret_cast<void *>(address), size));
}
KSharedMemory::~KSharedMemory() {
for (auto process : state.os->process_vec) {
user_pt_regs fregs = {0};
fregs.regs[0] = address;
fregs.regs[1] = size;
state.nce->ExecuteFunction(reinterpret_cast<void *>(UnmapFunc), fregs, process);
}
UnmapFunc(address, size);
close(fd);
}
u64 RemapFunc(u64 address, size_t old_size, size_t size) {
return reinterpret_cast<u64>(mremap(reinterpret_cast<void *>(address), old_size, size, 0));
}
void KSharedMemory::Resize(size_t new_size) {
for (auto process : state.os->process_vec) {
user_pt_regs fregs = {0};
fregs.regs[0] = address;
fregs.regs[1] = size;
fregs.regs[2] = new_size;
state.nce->ExecuteFunction(reinterpret_cast<void *>(RemapFunc), fregs, process);
if (reinterpret_cast<void *>(fregs.regs[0]) == MAP_FAILED)
throw exception("An error occurred while remapping shared region in child process");
}
if (RemapFunc(address, size, new_size) == reinterpret_cast<u64>(MAP_FAILED))
throw exception(fmt::format("An occurred while remapping shared region: {}", strerror(errno)));
size = new_size;
}
u64 UpdatePermissionFunc(u64 address, size_t size, u64 perms) {
return static_cast<u64>(mprotect(reinterpret_cast<void *>(address), size, static_cast<int>(perms)));
}
void KSharedMemory::UpdatePermission(bool local, Memory::Permission new_perms) {
for (auto process : state.os->process_vec) {
if ((local && process == owner_pid) || (!local && process != owner_pid)) {
user_pt_regs fregs = {0};
fregs.regs[0] = address;
fregs.regs[1] = size;
fregs.regs[2] = static_cast<u64>(new_perms.get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionFunc), fregs, process);
if (static_cast<int>(fregs.regs[0]) == -1)
throw exception("An error occurred while updating shared region's permissions in child process");
}
}
if ((local && owner_pid == 0) || (!local && owner_pid != 0))
if (mprotect(reinterpret_cast<void *>(address), size, new_perms.get()) == -1)
throw exception(fmt::format("An occurred while updating shared region's permissions: {}", strerror(errno)));
if (local)
local_permission = new_perms;
else
remote_permission = new_perms;
}
void KSharedMemory::InitiateProcess(pid_t pid) {
user_pt_regs fregs = {0};
fregs.regs[0] = address;
fregs.regs[1] = size;
fregs.regs[2] = static_cast<u64>(remote_permission.get());
state.nce->ExecuteFunction(reinterpret_cast<void *>(UpdatePermissionFunc), fregs, pid);
if (static_cast<int>(fregs.regs[0]) == -1)
throw exception("An error occurred while setting shared region's permissions in child process");
}
Memory::MemoryInfo KSharedMemory::GetInfo(pid_t pid) {
Memory::MemoryInfo info{};
info.base_address = address;
info.size = size;
info.type = static_cast<u64>(type);
info.memory_attribute.IsIpcLocked = (info.ipc_ref_count > 0);
info.memory_attribute.IsDeviceShared = (info.device_ref_count > 0);
info.perms = (pid == owner_pid) ? local_permission : remote_permission;
info.ipc_ref_count = ipc_ref_count;
info.device_ref_count = device_ref_count;
return info;
}
};