mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 07:34:19 +01:00
Implement svcSetMemoryPermission
Used by Minecraft legends, this should allow it to proceed further.
This commit is contained in:
parent
93fd0b2536
commit
1513e5f6df
@ -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();
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user