Implement svcSetMemoryPermission

Used by Minecraft legends, this should allow it to proceed further.
This commit is contained in:
TheASVigilante 2023-04-19 17:57:10 +02:00
parent 93fd0b2536
commit 1513e5f6df
5 changed files with 69 additions and 18 deletions

View File

@ -551,9 +551,13 @@ namespace skyline::kernel {
std::shared_lock lock(mutex);
size_t size{};
for (auto &chunk : chunks) {
if (chunk.second.state == memory::states::Heap)
size += chunk.second.size;
auto currChunk = upper_bound(heap.data());
if (heap.data() < currChunk->first) [[likely]]
--currChunk;
while (currChunk->first < heap.end().base()) {
if (currChunk->second.state == memory::states::Heap)
size += currChunk->second.size;
++currChunk;
}
return size + code.size() + state.process->mainThreadStack.size();

View File

@ -43,6 +43,47 @@ namespace skyline::kernel::svc {
Logger::Debug("Heap size changed to 0x{:X} bytes (0x{:X} - 0x{:X})", size, heapBaseAddr, heapBaseAddr + size);
}
void SetMemoryPermission(const DeviceState &state) {
u8 *address{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
if (!util::IsPageAligned(address)) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidAddress;
Logger::Warn("'address' not page aligned: 0x{:X}", address);
return;
}
u64 size{state.ctx->gpr.x1};
if (!size || !util::IsPageAligned(size)) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidSize;
Logger::Warn("'size' {}: 0x{:X}", size ? "is not page aligned" : "is zero", size);
return;
}
if (address >= (address + size) || !state.process->memory.AddressSpaceContains(span<u8>{address, size})) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidCurrentMemory;
Logger::Warn("Invalid address and size combination: 'address': 0x{:X}, 'size': 0x{:X} ", address, size);
return;
}
memory::Permission newPermission(static_cast<u8>(state.ctx->gpr.w2));
if ((!newPermission.r && newPermission.w) || newPermission.x) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
Logger::Warn("'permission' invalid: {}{}{}", newPermission.r ? 'R' : '-', newPermission.w ? 'W' : '-', newPermission.x ? 'X' : '-');
return;
}
auto chunk{state.process->memory.GetChunk(address).value()};
if (!chunk.second.state.permissionChangeAllowed) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidState;
Logger::Warn("Permission change not allowed for chunk at: 0x{:X}, state: 0x{:X}", chunk.first, chunk.second.state.value);
return;
}
state.process->memory.SetChunkPermission(span<u8>(address, size), newPermission);
Logger::Debug("Set permission to {}{}{} at 0x{:X} - 0x{:X} (0x{:X} bytes)", newPermission.r ? 'R' : '-', newPermission.w ? 'W' : '-', newPermission.x ? 'X' : '-', address, address + size, size);
state.ctx->gpr.w0 = Result{};
}
void SetMemoryAttribute(const DeviceState &state) {
u8 *address{reinterpret_cast<u8 *>(state.ctx->gpr.x0)};
if (!util::IsPageAligned(address)) [[unlikely]] {
@ -74,12 +115,12 @@ namespace skyline::kernel::svc {
return;
}
auto chunk{state.process->memory.GetChunk(address)};
auto chunk{state.process->memory.GetChunk(address).value()};
// We only check the first found chunk for whatever reason.
if (!chunk->second.state.attributeChangeAllowed) [[unlikely]] {
if (!chunk.second.state.attributeChangeAllowed) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidState;
Logger::Warn("Attribute change not allowed for chunk: 0x{:X}", chunk->first);
Logger::Warn("Attribute change not allowed for chunk: 0x{:X}", chunk.first);
return;
}
@ -164,15 +205,15 @@ namespace skyline::kernel::svc {
return;
}
auto dstChunk{state.process->memory.GetChunk(destination)};
while (dstChunk->second.state.value == memory::states::Unmapped)
dstChunk = state.process->memory.GetChunk(dstChunk->first + dstChunk->second.size);
auto dstChunk{state.process->memory.GetChunk(destination).value()};
while (dstChunk.second.state.value == memory::states::Unmapped)
dstChunk = state.process->memory.GetChunk(dstChunk.first + dstChunk.second.size).value();
if ((destination + size) > dstChunk->first) [[likely]] {
state.process->memory.SetChunkPermission(span<u8>{source + (dstChunk->first - destination), dstChunk->second.size}, dstChunk->second.permission);
state.process->memory.SetLockOnChunks(span<u8>{source + (dstChunk->first - destination), dstChunk->second.size}, false);
if ((destination + size) > dstChunk.first) [[likely]] {
state.process->memory.SetChunkPermission(span<u8>{source + (dstChunk.first - destination), dstChunk.second.size}, dstChunk.second.permission);
state.process->memory.SetLockOnChunks(span<u8>{source + (dstChunk.first - destination), dstChunk.second.size}, false);
std::memcpy(source + (dstChunk->first - destination), dstChunk->first, dstChunk->second.size);
std::memcpy(source + (dstChunk.first - destination), dstChunk.first, dstChunk.second.size);
state.process->memory.UnmapMemory(span<u8>{destination, size});
}

View File

@ -12,6 +12,12 @@ namespace skyline::kernel::svc {
*/
void SetHeapSize(const DeviceState &state);
/**
* @brief Reprotects a page-aligned memory region.
* @url https://switchbrew.org/wiki/SVC#SetMemoryPermission
*/
void SetMemoryPermission(const DeviceState &state);
/**
* @brief Change attribute of page-aligned memory region, this is used to turn on/off caching for a given memory area
* @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute
@ -20,7 +26,7 @@ namespace skyline::kernel::svc {
/**
* @brief Maps a memory range into a different range, mainly used for adding guard pages around stack
* @url https://switchbrew.org/wiki/SVC#SetMemoryAttribute
* @url https://switchbrew.org/wiki/SVC#MapMemory
*/
void MapMemory(const DeviceState &state);
@ -115,7 +121,7 @@ namespace skyline::kernel::svc {
void UnmapSharedMemory(const DeviceState &state);
/**
* @brief Returns a handle to a KSharedMemory object
* @brief Returns a handle to a KTransferMemory object
* @url https://switchbrew.org/wiki/SVC#CreateTransferMemory
*/
void CreateTransferMemory(const DeviceState &state);
@ -269,7 +275,7 @@ namespace skyline::kernel::svc {
static constexpr std::array<SvcDescriptor, 0x80> SvcTable{
SVC_NONE, // 0x00 (Does not exist)
SVC_ENTRY(SetHeapSize), // 0x01
SVC_NONE, // 0x02
SVC_ENTRY(SetMemoryPermission), // 0x02
SVC_ENTRY(SetMemoryAttribute), // 0x03
SVC_ENTRY(MapMemory), // 0x04
SVC_ENTRY(UnmapMemory), // 0x05

View File

@ -28,7 +28,7 @@ namespace skyline::kernel::type {
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]]
if (mmap(map.data(), map.size(), permission.Get() ? PROT_READ | PROT_WRITE : 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;

View File

@ -45,7 +45,7 @@ namespace skyline::kernel::type {
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]]
if (mmap(guest.data(), guest.size(), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS | MAP_POPULATE, -1, 0) == MAP_FAILED) [[unlikely]]
Logger::Warn("An error occurred while unmapping transfer memory in guest: {}", strerror(errno));
switch (originalMapping.state.type) {