mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-11-26 11:24:15 +01:00
General memman fixups & address review
This commit is contained in:
parent
1513e5f6df
commit
850f616dc7
@ -7,46 +7,36 @@
|
|||||||
#include "types/KProcess.h"
|
#include "types/KProcess.h"
|
||||||
|
|
||||||
namespace skyline::kernel {
|
namespace skyline::kernel {
|
||||||
MemoryManager::MemoryManager(const DeviceState &state) noexcept : state(state), setHeapSize(), chunks() {}
|
MemoryManager::MemoryManager(const DeviceState &state) noexcept : state{state}, processHeapSize{}, memRefs{} {}
|
||||||
|
|
||||||
MemoryManager::~MemoryManager() noexcept {
|
MemoryManager::~MemoryManager() noexcept {
|
||||||
if (base.valid() && !base.empty())
|
if (base.valid() && !base.empty())
|
||||||
munmap(reinterpret_cast<void *>(base.data()), base.size());
|
munmap(reinterpret_cast<void *>(base.data()), base.size());
|
||||||
|
if (addressSpaceType != memory::AddressSpaceType::AddressSpace39Bit)
|
||||||
|
if (codeBase36Bit.valid() && !codeBase36Bit.empty())
|
||||||
|
munmap(reinterpret_cast<void *>(codeBase36Bit.data()), codeBase36Bit.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<u8 *,ChunkDescriptor>::iterator MemoryManager::upper_bound(u8 *address) {
|
void MemoryManager::MapInternal(const std::pair<u8 *, ChunkDescriptor> &newDesc) {
|
||||||
std::map<u8 *,ChunkDescriptor>::iterator result{chunks.begin()};
|
|
||||||
|
|
||||||
if (chunks.size() != 1) [[likely]]
|
|
||||||
while (result->first <= address) {
|
|
||||||
++result;
|
|
||||||
if (result->first + result->second.size == addressSpace.end().base())
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MemoryManager::MapInternal(std::pair<u8 *, ChunkDescriptor> *newDesc) {
|
|
||||||
// The chunk that contains / precedes the new chunk base address
|
// The chunk that contains / precedes the new chunk base address
|
||||||
auto firstChunkBase{upper_bound(newDesc->first)};
|
auto firstChunkBase{chunks.lower_bound(newDesc.first)};
|
||||||
while (newDesc->first <= firstChunkBase->first)
|
if (newDesc.first <= firstChunkBase->first)
|
||||||
--firstChunkBase;
|
--firstChunkBase;
|
||||||
|
|
||||||
// The chunk that contains / follows the end address of the new chunk
|
// The chunk that contains / follows the end address of the new chunk
|
||||||
auto lastChunkBase{upper_bound(newDesc->first + newDesc->second.size)};
|
auto lastChunkBase{chunks.lower_bound(newDesc.first + newDesc.second.size)};
|
||||||
while ((newDesc->first + newDesc->second.size) < lastChunkBase->first)
|
if ((newDesc.first + newDesc.second.size) < lastChunkBase->first)
|
||||||
--lastChunkBase;
|
--lastChunkBase;
|
||||||
|
|
||||||
ChunkDescriptor firstChunk{firstChunkBase->second};
|
ChunkDescriptor firstChunk{firstChunkBase->second};
|
||||||
ChunkDescriptor lastChunk{lastChunkBase->second};
|
ChunkDescriptor lastChunk{lastChunkBase->second};
|
||||||
|
|
||||||
bool needsReprotection{false};
|
bool needsReprotection{false};
|
||||||
bool isUnmapping{newDesc->second.state == memory::states::Unmapped};
|
bool isUnmapping{newDesc.second.state == memory::states::Unmapped};
|
||||||
|
|
||||||
// We cut a hole in a single chunk
|
// We cut a hole in a single chunk
|
||||||
if (firstChunkBase->first == lastChunkBase->first) {
|
if (firstChunkBase->first == lastChunkBase->first) {
|
||||||
if (firstChunk.IsCompatible(newDesc->second)) [[unlikely]]
|
if (firstChunk.IsCompatible(newDesc.second)) [[unlikely]]
|
||||||
// No editing necessary
|
// No editing necessary
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -54,15 +44,15 @@ namespace skyline::kernel {
|
|||||||
needsReprotection = true;
|
needsReprotection = true;
|
||||||
|
|
||||||
// We edit the chunk's first half
|
// We edit the chunk's first half
|
||||||
firstChunk.size = static_cast<size_t>(newDesc->first - firstChunkBase->first);
|
firstChunk.size = static_cast<size_t>(newDesc.first - firstChunkBase->first);
|
||||||
chunks[firstChunkBase->first] = firstChunk;
|
chunks[firstChunkBase->first] = firstChunk;
|
||||||
|
|
||||||
// We create the chunk's second half
|
// We create the chunk's second half
|
||||||
lastChunk.size = static_cast<size_t>((lastChunkBase->first + lastChunk.size) - (newDesc->first + newDesc->second.size));
|
lastChunk.size = static_cast<size_t>((lastChunkBase->first + lastChunk.size) - (newDesc.first + newDesc.second.size));
|
||||||
chunks[newDesc->first + newDesc->second.size] = lastChunk;
|
chunks.insert({newDesc.first + newDesc.second.size, lastChunk});
|
||||||
|
|
||||||
// Insert new chunk in between
|
// Insert new chunk in between
|
||||||
chunks[newDesc->first] = newDesc->second;
|
chunks.insert(newDesc);
|
||||||
} else {
|
} else {
|
||||||
// If there are descriptors between first and last chunk, delete them
|
// If there are descriptors between first and last chunk, delete them
|
||||||
if ((firstChunkBase->first + firstChunk.size) != lastChunkBase->first) {
|
if ((firstChunkBase->first + firstChunk.size) != lastChunkBase->first) {
|
||||||
@ -79,13 +69,13 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
bool shouldInsert{true};
|
bool shouldInsert{true};
|
||||||
|
|
||||||
if (firstChunk.IsCompatible(newDesc->second)) {
|
if (firstChunk.IsCompatible(newDesc.second)) {
|
||||||
shouldInsert = false;
|
shouldInsert = false;
|
||||||
|
|
||||||
firstChunk.size = static_cast<size_t>((newDesc->first + newDesc->second.size) - firstChunkBase->first);
|
firstChunk.size = static_cast<size_t>((newDesc.first + newDesc.second.size) - firstChunkBase->first);
|
||||||
chunks[firstChunkBase->first] = firstChunk;
|
chunks[firstChunkBase->first] = firstChunk;
|
||||||
} else if ((firstChunkBase->first + firstChunk.size) != newDesc->first) {
|
} else if ((firstChunkBase->first + firstChunk.size) != newDesc.first) {
|
||||||
firstChunk.size = static_cast<size_t>(newDesc->first - firstChunkBase->first);
|
firstChunk.size = static_cast<size_t>(newDesc.first - firstChunkBase->first);
|
||||||
|
|
||||||
chunks[firstChunkBase->first] = firstChunk;
|
chunks[firstChunkBase->first] = firstChunk;
|
||||||
|
|
||||||
@ -93,25 +83,25 @@ namespace skyline::kernel {
|
|||||||
needsReprotection = true;
|
needsReprotection = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastChunk.IsCompatible(newDesc->second)) {
|
if (lastChunk.IsCompatible(newDesc.second)) {
|
||||||
u8 *oldBase{lastChunkBase->first};
|
u8 *oldBase{lastChunkBase->first};
|
||||||
chunks.erase(lastChunkBase);
|
chunks.erase(lastChunkBase);
|
||||||
|
|
||||||
if (shouldInsert) {
|
if (shouldInsert) {
|
||||||
shouldInsert = false;
|
shouldInsert = false;
|
||||||
|
|
||||||
lastChunk.size = static_cast<size_t>((lastChunk.size + oldBase) - (newDesc->first));
|
lastChunk.size = static_cast<size_t>((lastChunk.size + oldBase) - (newDesc.first));
|
||||||
|
|
||||||
chunks[newDesc->first] = lastChunk;
|
chunks[newDesc.first] = lastChunk;
|
||||||
} else {
|
} else {
|
||||||
firstChunk.size = static_cast<size_t>((lastChunk.size + oldBase) - firstChunkBase->first);
|
firstChunk.size = static_cast<size_t>((lastChunk.size + oldBase) - firstChunkBase->first);
|
||||||
chunks[firstChunkBase->first] = firstChunk;
|
chunks[firstChunkBase->first] = firstChunk;
|
||||||
}
|
}
|
||||||
} else if ((newDesc->first + newDesc->second.size) != lastChunkBase->first) {
|
} else if ((newDesc.first + newDesc.second.size) != lastChunkBase->first) {
|
||||||
lastChunk.size = static_cast<size_t>((lastChunk.size + lastChunkBase->first) - (newDesc->first + newDesc->second.size));
|
lastChunk.size = static_cast<size_t>((lastChunk.size + lastChunkBase->first) - (newDesc.first + newDesc.second.size));
|
||||||
|
|
||||||
chunks.erase(lastChunkBase);
|
chunks.erase(lastChunkBase);
|
||||||
chunks[newDesc->first + newDesc->second.size] = lastChunk;
|
chunks[newDesc.first + newDesc.second.size] = lastChunk;
|
||||||
|
|
||||||
if ((lastChunk.state == memory::states::Unmapped) != isUnmapping)
|
if ((lastChunk.state == memory::states::Unmapped) != isUnmapping)
|
||||||
needsReprotection = true;
|
needsReprotection = true;
|
||||||
@ -119,49 +109,45 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
// Insert if not merged
|
// Insert if not merged
|
||||||
if (shouldInsert)
|
if (shouldInsert)
|
||||||
chunks[newDesc->first] = newDesc->second;
|
chunks.insert(newDesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsReprotection)
|
if (needsReprotection)
|
||||||
if (mprotect(newDesc->first, newDesc->second.size, !isUnmapping ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE)) [[unlikely]]
|
if (mprotect(newDesc.first, newDesc.second.size, !isUnmapping ? PROT_READ | PROT_WRITE | PROT_EXEC : PROT_NONE)) [[unlikely]]
|
||||||
Logger::Warn("Reprotection failed: {}", strerror(errno));
|
Logger::Warn("Reprotection failed: {}", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::ForeachChunkinRange(span<skyline::u8> memory, auto editCallback) {
|
void MemoryManager::ForeachChunkinRange(span<u8> memory, auto editCallback) {
|
||||||
auto chunkBase{upper_bound(memory.data())};
|
auto chunkBase{chunks.lower_bound(memory.data())};
|
||||||
if (memory.data() < chunkBase->first)
|
if (memory.data() < chunkBase->first)
|
||||||
--chunkBase;
|
--chunkBase;
|
||||||
|
|
||||||
ChunkDescriptor resultChunk{chunkBase->second};
|
|
||||||
|
|
||||||
size_t sizeLeft{memory.size()};
|
size_t sizeLeft{memory.size()};
|
||||||
|
|
||||||
if (chunkBase->first < memory.data()) {
|
if (chunkBase->first < memory.data()) [[unlikely]] {
|
||||||
size_t copySize{std::min<size_t>(resultChunk.size - (static_cast<size_t>(memory.data() - chunkBase->first)), memory.size())};
|
size_t chunkSize{std::min<size_t>(chunkBase->second.size - (static_cast<size_t>(memory.data() - chunkBase->first)), memory.size())};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(memory.data(), resultChunk);
|
std::pair<u8 *, ChunkDescriptor> temp{memory.data(), chunkBase->second};
|
||||||
temp.second.size = copySize;
|
temp.second.size = chunkSize;
|
||||||
editCallback(temp);
|
editCallback(temp);
|
||||||
|
|
||||||
++chunkBase;
|
++chunkBase;
|
||||||
resultChunk = chunkBase->second;
|
sizeLeft -= chunkSize;
|
||||||
sizeLeft -= copySize;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while (sizeLeft) {
|
while (sizeLeft) {
|
||||||
if (sizeLeft < resultChunk.size) {
|
if (sizeLeft < chunkBase->second.size) {
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(chunkBase->first, resultChunk);
|
std::pair<u8 *, ChunkDescriptor> temp(*chunkBase);
|
||||||
temp.second.size = sizeLeft;
|
temp.second.size = sizeLeft;
|
||||||
editCallback(temp);
|
editCallback(temp);
|
||||||
break;
|
break;
|
||||||
} else [[likely]] {
|
} else [[likely]] {
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(chunkBase->first, resultChunk);
|
std::pair<u8 *, ChunkDescriptor> temp(*chunkBase);
|
||||||
|
|
||||||
editCallback(temp);
|
editCallback(temp);
|
||||||
|
|
||||||
sizeLeft = sizeLeft - resultChunk.size;
|
sizeLeft = sizeLeft - chunkBase->second.size;
|
||||||
++chunkBase;
|
++chunkBase;
|
||||||
resultChunk = chunkBase->second;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,32 +219,27 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
// Qualcomm KGSL (Kernel Graphic Support Layer/Kernel GPU driver) maps below 35-bits, reserving it causes KGSL to go OOM
|
// Qualcomm KGSL (Kernel Graphic Support Layer/Kernel GPU driver) maps below 35-bits, reserving it causes KGSL to go OOM
|
||||||
static constexpr size_t KgslReservedRegionSize{1ULL << 35};
|
static constexpr size_t KgslReservedRegionSize{1ULL << 35};
|
||||||
|
|
||||||
|
base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false);
|
||||||
|
|
||||||
if (type != memory::AddressSpaceType::AddressSpace36Bit) {
|
if (type != memory::AddressSpaceType::AddressSpace36Bit) {
|
||||||
base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false);
|
|
||||||
|
|
||||||
chunks[addressSpace.data()] = ChunkDescriptor{
|
|
||||||
.size = addressSpace.size(),
|
|
||||||
.state = memory::states::Unmapped,
|
|
||||||
};
|
|
||||||
|
|
||||||
code = base;
|
code = base;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
codeBase36Bit = AllocateMappedRange(0x78000000, RegionAlignment, 0x8000000, KgslReservedRegionSize, false);
|
code = codeBase36Bit = AllocateMappedRange(0x78000000, RegionAlignment, 0x8000000, KgslReservedRegionSize, false);
|
||||||
base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false);
|
|
||||||
|
|
||||||
if ((reinterpret_cast<u64>(base.data()) + baseSize) > (1ULL << 36)) {
|
if ((reinterpret_cast<u64>(base.data()) + baseSize) > (1ULL << 36)) {
|
||||||
Logger::Warn("Couldn't fit regions into AS! Resizing AS instead!");
|
Logger::Warn("Couldn't fit regions into 36 bit AS! Resizing AS to 39 bits!");
|
||||||
addressSpace = span<u8>{reinterpret_cast<u8 *>(0), 1ULL << 39};
|
addressSpace = span<u8>{reinterpret_cast<u8 *>(0), 1ULL << 39};
|
||||||
}
|
}
|
||||||
|
|
||||||
chunks[addressSpace.data()] = ChunkDescriptor{
|
|
||||||
.size = addressSpace.size(),
|
|
||||||
.state = memory::states::Unmapped,
|
|
||||||
};
|
|
||||||
|
|
||||||
code = codeBase36Bit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert a placeholder element at the end of the map to make sure upper_bound/lower_bound never triggers std::map::end() which is broken
|
||||||
|
chunks = {{addressSpace.data(),{
|
||||||
|
.size = addressSpace.size(),
|
||||||
|
.state = memory::states::Unmapped,
|
||||||
|
}}, {reinterpret_cast<u8 *>(UINT64_MAX), {
|
||||||
|
.state = memory::states::Reserved,
|
||||||
|
}}};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::InitializeRegions(span<u8> codeRegion) {
|
void MemoryManager::InitializeRegions(span<u8> codeRegion) {
|
||||||
@ -270,11 +251,10 @@ namespace skyline::kernel {
|
|||||||
|
|
||||||
// As a workaround if we can't place the code region at the base of the AS we mark it as inaccessible heap so rtld doesn't crash
|
// As a workaround if we can't place the code region at the base of the AS we mark it as inaccessible heap so rtld doesn't crash
|
||||||
if (codeBase36Bit.data() != reinterpret_cast<u8 *>(0x8000000)) {
|
if (codeBase36Bit.data() != reinterpret_cast<u8 *>(0x8000000)) {
|
||||||
std::pair<u8 *, ChunkDescriptor> tmp(reinterpret_cast<u8 *>(0x8000000), ChunkDescriptor{
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(reinterpret_cast<u8 *>(0x8000000),{
|
||||||
.size = reinterpret_cast<size_t>(codeBase36Bit.data() - 0x8000000),
|
.size = reinterpret_cast<size_t>(codeBase36Bit.data() - 0x8000000),
|
||||||
.state = memory::states::Heap,
|
.state = memory::states::Heap
|
||||||
});
|
}));
|
||||||
MapInternal(&tmp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place code, stack and TLS/IO in the lower 36-bits of the host AS and heap and alias past that
|
// Place code, stack and TLS/IO in the lower 36-bits of the host AS and heap and alias past that
|
||||||
@ -298,7 +278,7 @@ namespace skyline::kernel {
|
|||||||
if (newSize > base.size()) [[unlikely]]
|
if (newSize > base.size()) [[unlikely]]
|
||||||
throw exception("Guest VMM size has exceeded host carveout size: 0x{:X}/0x{:X} (Code: 0x{:X}/0x{:X})", newSize, base.size(), code.size(), CodeRegionSize);
|
throw exception("Guest VMM size has exceeded host carveout size: 0x{:X}/0x{:X} (Code: 0x{:X}/0x{:X})", newSize, base.size(), code.size(), CodeRegionSize);
|
||||||
|
|
||||||
if (newSize != base.size())
|
if (newSize != base.size()) [[likely]]
|
||||||
munmap(base.end().base(), newSize - base.size());
|
munmap(base.end().base(), newSize - base.size());
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -364,166 +344,151 @@ namespace skyline::kernel {
|
|||||||
return span<u8>{reinterpret_cast<u8 *>(mirrorBase), totalSize};
|
return span<u8>{reinterpret_cast<u8 *>(mirrorBase), totalSize};
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::SetLockOnChunks(span<u8> memory, bool value) {
|
void MemoryManager::SetRegionBorrowed(span<u8> memory, bool value) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
|
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
|
||||||
desc.second.attributes.isBorrowed = value;
|
desc.second.attributes.isBorrowed = value;
|
||||||
MapInternal(&desc);
|
MapInternal(desc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::SetCPUCachingOnChunks(span<u8> memory, bool value) {
|
void MemoryManager::SetRegionCpuCaching(span<u8> memory, bool value) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
|
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
|
||||||
desc.second.attributes.isUncached = value;
|
desc.second.attributes.isUncached = value;
|
||||||
MapInternal(&desc);
|
MapInternal(desc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::SetChunkPermission(span<u8> memory, memory::Permission permission) {
|
void MemoryManager::SetRegionPermission(span<u8> memory, memory::Permission permission) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
|
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
|
||||||
desc.second.permission = permission;
|
desc.second.permission = permission;
|
||||||
MapInternal(&desc);
|
MapInternal(desc);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<std::pair<u8 *, ChunkDescriptor>> MemoryManager::GetChunk(u8 *addr) {
|
std::optional<std::pair<u8 *, ChunkDescriptor>> MemoryManager::GetChunk(u8 *addr) {
|
||||||
std::shared_lock lock(mutex);
|
std::shared_lock lock{mutex};
|
||||||
|
|
||||||
if (!addressSpace.contains(addr)) [[unlikely]]
|
if (!addressSpace.contains(addr)) [[unlikely]]
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
auto chunkBase = upper_bound(addr);
|
auto chunkBase = chunks.lower_bound(addr);
|
||||||
if (addr < chunkBase->first) [[likely]]
|
if (addr < chunkBase->first)
|
||||||
--chunkBase;
|
--chunkBase;
|
||||||
|
|
||||||
return std::make_optional(*chunkBase);
|
return std::make_optional(*chunkBase);
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapCodeMemory(span<u8> memory, memory::Permission permission) {
|
__attribute__((always_inline)) void MemoryManager::MapCodeMemory(span<u8> memory, memory::Permission permission) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = permission,
|
.permission = permission,
|
||||||
.state = memory::states::Code});
|
.state = memory::states::Code
|
||||||
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapMutableCodeMemory(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::MapMutableCodeMemory(span<u8> memory) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = {true, true, false},
|
.permission = {true, true, false},
|
||||||
.state = memory::states::CodeMutable});
|
.state = memory::states::CodeMutable
|
||||||
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapStackMemory(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::MapStackMemory(span<u8> memory) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = {true, true, false},
|
.permission = {true, true, false},
|
||||||
.state = memory::states::Stack,
|
.state = memory::states::Stack,
|
||||||
.isSrcMergeDisallowed = true});
|
.isSrcMergeDisallowed = true
|
||||||
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapHeapMemory(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::MapHeapMemory(span<u8> memory) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = {true, true, false},
|
.permission = {true, true, false},
|
||||||
.state = memory::states::Heap});
|
.state = memory::states::Heap
|
||||||
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapSharedMemory(span<u8> memory, memory::Permission permission) {
|
__attribute__((always_inline)) void MemoryManager::MapSharedMemory(span<u8> memory, memory::Permission permission) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = permission,
|
.permission = permission,
|
||||||
.state = memory::states::SharedMemory,
|
.state = memory::states::SharedMemory,
|
||||||
.isSrcMergeDisallowed = true});
|
.isSrcMergeDisallowed = true
|
||||||
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapTransferMemory(span<u8> memory, memory::Permission permission) {
|
__attribute__((always_inline)) void MemoryManager::MapTransferMemory(span<u8> memory, memory::Permission permission) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = permission,
|
.permission = permission,
|
||||||
.state = permission.raw ? memory::states::TransferMemory : memory::states::TransferMemoryIsolated,
|
.state = permission.raw ? memory::states::TransferMemory : memory::states::TransferMemoryIsolated,
|
||||||
.isSrcMergeDisallowed = true});
|
.isSrcMergeDisallowed = true
|
||||||
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::MapThreadLocalMemory(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::MapThreadLocalMemory(span<u8> memory) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = {true, true, false},
|
.permission = {true, true, false},
|
||||||
.state = memory::states::ThreadLocal});
|
.state = memory::states::ThreadLocal
|
||||||
MapInternal(&temp);
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::Reserve(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::Reserve(span<u8> memory) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
|
||||||
.size = memory.size(),
|
.size = memory.size(),
|
||||||
.permission = {false, false, false},
|
.permission = {false, false, false},
|
||||||
.state = memory::states::Reserved});
|
.state = memory::states::Reserved
|
||||||
MapInternal(&temp);
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::UnmapMemory(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::UnmapMemory(span<u8> memory) {
|
||||||
std::unique_lock lock(mutex);
|
std::unique_lock lock{mutex};
|
||||||
|
|
||||||
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) {
|
ForeachChunkinRange(memory, [&](const std::pair<u8 *, ChunkDescriptor> &desc) {
|
||||||
if (desc.second.state != memory::states::Unmapped)
|
if (desc.second.state != memory::states::Unmapped)
|
||||||
FreeMemory(span<u8>((u8 *)desc.first, desc.second.size));
|
FreeMemory(span<u8>(desc.first, desc.second.size));
|
||||||
});
|
});
|
||||||
|
|
||||||
std::pair<u8 *, ChunkDescriptor> temp(
|
MapInternal(std::pair<u8 *, ChunkDescriptor>(
|
||||||
memory.data(),
|
memory.data(),{
|
||||||
ChunkDescriptor{
|
.size = memory.size(),
|
||||||
.size = memory.size(),
|
.permission = {false, false, false},
|
||||||
.permission = {false, false, false},
|
.state = memory::states::Unmapped
|
||||||
.state = memory::states::Unmapped});
|
}));
|
||||||
MapInternal(&temp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((always_inline)) void MemoryManager::FreeMemory(span<u8> memory) {
|
__attribute__((always_inline)) void MemoryManager::FreeMemory(span<u8> memory) {
|
||||||
@ -535,25 +500,23 @@ namespace skyline::kernel {
|
|||||||
Logger::Error("Failed to free memory: {}", strerror(errno));
|
Logger::Error("Failed to free memory: {}", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::AddRef(const std::shared_ptr<type::KMemory> &ptr) {
|
void MemoryManager::AddRef(std::shared_ptr<type::KMemory> ptr) {
|
||||||
memRefs.push_back(ptr);
|
memRefs.push_back(std::move(ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MemoryManager::RemoveRef(const std::shared_ptr<type::KMemory> &ptr) {
|
void MemoryManager::RemoveRef(std::shared_ptr<type::KMemory> ptr) {
|
||||||
std::vector<std::shared_ptr<type::KMemory>>::iterator i{std::find(memRefs.begin(), memRefs.end(), ptr)};
|
auto i = std::find(memRefs.begin(), memRefs.end(), ptr);
|
||||||
|
|
||||||
if (*i == ptr) {
|
if (*i == ptr) [[likely]]
|
||||||
memRefs.erase(i);
|
memRefs.erase(i);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t MemoryManager::GetUserMemoryUsage() {
|
size_t MemoryManager::GetUserMemoryUsage() {
|
||||||
std::shared_lock lock(mutex);
|
std::shared_lock lock{mutex};
|
||||||
size_t size{};
|
size_t size{};
|
||||||
|
|
||||||
auto currChunk = upper_bound(heap.data());
|
auto currChunk = chunks.lower_bound(heap.data());
|
||||||
if (heap.data() < currChunk->first) [[likely]]
|
|
||||||
--currChunk;
|
|
||||||
while (currChunk->first < heap.end().base()) {
|
while (currChunk->first < heap.end().base()) {
|
||||||
if (currChunk->second.state == memory::states::Heap)
|
if (currChunk->second.state == memory::states::Heap)
|
||||||
size += currChunk->second.size;
|
size += currChunk->second.size;
|
||||||
@ -564,7 +527,7 @@ namespace skyline::kernel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t MemoryManager::GetSystemResourceUsage() {
|
size_t MemoryManager::GetSystemResourceUsage() {
|
||||||
std::shared_lock lock(mutex);
|
std::shared_lock lock{mutex};
|
||||||
constexpr size_t KMemoryBlockSize{0x40};
|
constexpr size_t KMemoryBlockSize{0x40};
|
||||||
return std::min(static_cast<size_t>(state.process->npdm.meta.systemResourceSize), util::AlignUp(chunks.size() * KMemoryBlockSize, constant::PageSize));
|
return std::min(static_cast<size_t>(state.process->npdm.meta.systemResourceSize), util::AlignUp(chunks.size() * KMemoryBlockSize, constant::PageSize));
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,19 @@ namespace skyline {
|
|||||||
/**
|
/**
|
||||||
* @brief Initializes all permissions to false
|
* @brief Initializes all permissions to false
|
||||||
*/
|
*/
|
||||||
constexpr Permission() : raw() {}
|
constexpr Permission() : raw{} {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Initializes permissions where the first three bits correspond to RWX
|
* @brief Initializes permissions where the first three bits correspond to RWX
|
||||||
*/
|
*/
|
||||||
constexpr explicit Permission(u8 raw) : raw(raw) {}
|
constexpr explicit Permission(u8 raw) : raw{raw} {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param read If memory has read permission
|
* @param read If memory has read permission
|
||||||
* @param write If memory has write permission
|
* @param write If memory has write permission
|
||||||
* @param execute If memory has execute permission
|
* @param execute If memory has execute permission
|
||||||
*/
|
*/
|
||||||
constexpr Permission(bool read, bool write, bool execute) : r(read), w(write), x(execute) {}
|
constexpr Permission(bool read, bool write, bool execute) : r{read}, w{write}, x{execute} {}
|
||||||
|
|
||||||
inline bool operator==(const Permission &rhs) const { return r == rhs.r && w == rhs.w && x == rhs.x; }
|
inline bool operator==(const Permission &rhs) const { return r == rhs.r && w == rhs.w && x == rhs.x; }
|
||||||
|
|
||||||
@ -63,13 +63,18 @@ namespace skyline {
|
|||||||
* @url https://switchbrew.org/wiki/SVC#MemoryAttribute
|
* @url https://switchbrew.org/wiki/SVC#MemoryAttribute
|
||||||
*/
|
*/
|
||||||
union MemoryAttribute {
|
union MemoryAttribute {
|
||||||
|
|
||||||
|
constexpr MemoryAttribute() : value{} {}
|
||||||
|
|
||||||
|
constexpr explicit MemoryAttribute(u8 value) : value{value} {}
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool isBorrowed : 1; //!< This is required for async IPC user buffers
|
bool isBorrowed : 1; //!< This is required for async IPC user buffers
|
||||||
bool isIpcLocked : 1; //!< True when IpcRefCount > 0
|
bool isIpcLocked : 1; //!< True when IpcRefCount > 0
|
||||||
bool isDeviceShared : 1; //!< True when DeviceRefCount > 0
|
bool isDeviceShared : 1; //!< True when DeviceRefCount > 0
|
||||||
bool isUncached : 1; //!< This is used to disable memory caching to share memory with the GPU
|
bool isUncached : 1; //!< This is used to disable memory caching to share memory with the GPU
|
||||||
};
|
};
|
||||||
u32 value{};
|
u8 value;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,9 +127,9 @@ namespace skyline {
|
|||||||
* @url https://switchbrew.org/wiki/SVC#MemoryState
|
* @url https://switchbrew.org/wiki/SVC#MemoryState
|
||||||
*/
|
*/
|
||||||
union MemoryState {
|
union MemoryState {
|
||||||
constexpr MemoryState(const u32 value) : value(value) {}
|
constexpr MemoryState(const u32 value) : value{value} {}
|
||||||
|
|
||||||
constexpr MemoryState() : value(0) {}
|
constexpr MemoryState() : value{} {}
|
||||||
|
|
||||||
constexpr bool operator==(const MemoryState &other) const {
|
constexpr bool operator==(const MemoryState &other) const {
|
||||||
return value == other.value;
|
return value == other.value;
|
||||||
@ -156,7 +161,7 @@ namespace skyline {
|
|||||||
bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block
|
bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block
|
||||||
bool isLinearMapped : 1; //!< If this block is mapped linearly
|
bool isLinearMapped : 1; //!< If this block is mapped linearly
|
||||||
};
|
};
|
||||||
u32 value{};
|
u32 value;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(MemoryState) == sizeof(u32));
|
static_assert(sizeof(MemoryState) == sizeof(u32));
|
||||||
|
|
||||||
@ -202,10 +207,10 @@ namespace skyline {
|
|||||||
namespace kernel {
|
namespace kernel {
|
||||||
struct ChunkDescriptor {
|
struct ChunkDescriptor {
|
||||||
bool isSrcMergeDisallowed;
|
bool isSrcMergeDisallowed;
|
||||||
size_t size;
|
|
||||||
memory::Permission permission;
|
memory::Permission permission;
|
||||||
memory::MemoryState state;
|
|
||||||
memory::MemoryAttribute attributes;
|
memory::MemoryAttribute attributes;
|
||||||
|
memory::MemoryState state;
|
||||||
|
size_t size;
|
||||||
|
|
||||||
constexpr bool IsCompatible(const ChunkDescriptor &chunk) const noexcept {
|
constexpr bool IsCompatible(const ChunkDescriptor &chunk) const noexcept {
|
||||||
return chunk.permission == permission && chunk.state.value == state.value && chunk.attributes.value == attributes.value && !isSrcMergeDisallowed;
|
return chunk.permission == permission && chunk.state.value == state.value && chunk.attributes.value == attributes.value && !isSrcMergeDisallowed;
|
||||||
@ -222,10 +227,7 @@ namespace skyline {
|
|||||||
|
|
||||||
std::vector<std::shared_ptr<type::KMemory>> memRefs;
|
std::vector<std::shared_ptr<type::KMemory>> memRefs;
|
||||||
|
|
||||||
// Workaround for broken std implementation
|
void MapInternal(const std::pair<u8 *, ChunkDescriptor> &newDesc);
|
||||||
std::map<u8 *, ChunkDescriptor>::iterator upper_bound(u8 *address);
|
|
||||||
|
|
||||||
void MapInternal(std::pair<u8 *, ChunkDescriptor> *newDesc);
|
|
||||||
|
|
||||||
void ForeachChunkinRange(span<u8> memory, auto editCallback);
|
void ForeachChunkinRange(span<u8> memory, auto editCallback);
|
||||||
|
|
||||||
@ -240,7 +242,7 @@ namespace skyline {
|
|||||||
span<u8> stack{};
|
span<u8> stack{};
|
||||||
span<u8> tlsIo{}; //!< TLS/IO
|
span<u8> tlsIo{}; //!< TLS/IO
|
||||||
|
|
||||||
size_t setHeapSize; //!< For use by svcSetHeapSize
|
size_t processHeapSize; //!< For use by svcSetHeapSize
|
||||||
|
|
||||||
std::shared_mutex mutex; //!< Synchronizes any operations done on the VMM, it's locked in shared mode by readers and exclusive mode by writers
|
std::shared_mutex mutex; //!< Synchronizes any operations done on the VMM, it's locked in shared mode by readers and exclusive mode by writers
|
||||||
|
|
||||||
@ -272,27 +274,27 @@ namespace skyline {
|
|||||||
span<u8> CreateMirrors(const std::vector<span<u8>> ®ions);
|
span<u8> CreateMirrors(const std::vector<span<u8>> ®ions);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the attributes for chunks within a certain range
|
* @brief Sets the isBorrowed attribute for chunks within a certain range
|
||||||
*/
|
*/
|
||||||
void SetLockOnChunks(span<u8> memory, bool value);
|
void SetRegionBorrowed(span<u8> memory, bool value);
|
||||||
|
|
||||||
void SetCPUCachingOnChunks(span<u8> memory, bool value);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Sets the permission for chunks within a certain range
|
* @brief Sets the isUncached attribute for chunks within a certain range
|
||||||
|
*/
|
||||||
|
void SetRegionCpuCaching(span<u8> memory, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the permissions for chunks within a certain range
|
||||||
* @note The permissions set here are not accurate to the actual permissions set on the chunk and are only for the guest
|
* @note The permissions set here are not accurate to the actual permissions set on the chunk and are only for the guest
|
||||||
*/
|
*/
|
||||||
void SetChunkPermission(span<u8> memory, memory::Permission permission);
|
void SetRegionPermission(span<u8> memory, memory::Permission permission);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gets the highest chunk's descriptor that contains this address
|
* @brief Gets the highest chunk's descriptor that contains this address
|
||||||
*/
|
*/
|
||||||
std::optional<std::pair<u8 *, ChunkDescriptor>> GetChunk(u8 *addr);
|
std::optional<std::pair<u8 *, ChunkDescriptor>> GetChunk(u8 *addr);
|
||||||
|
|
||||||
/**
|
// Various mapping functions for use by the guest
|
||||||
* Various mapping functions for use by the guest
|
|
||||||
* @note UnmapMemory frees the underlying memory as well
|
|
||||||
*/
|
|
||||||
void MapCodeMemory(span<u8> memory, memory::Permission permission);
|
void MapCodeMemory(span<u8> memory, memory::Permission permission);
|
||||||
|
|
||||||
void MapMutableCodeMemory(span<u8> memory);
|
void MapMutableCodeMemory(span<u8> memory);
|
||||||
@ -309,6 +311,9 @@ namespace skyline {
|
|||||||
|
|
||||||
void Reserve(span<u8> memory);
|
void Reserve(span<u8> memory);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note `UnmapMemory` also calls `FreeMemory` on the unmapped memory range
|
||||||
|
*/
|
||||||
void UnmapMemory(span<u8> memory);
|
void UnmapMemory(span<u8> memory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -318,11 +323,14 @@ namespace skyline {
|
|||||||
void FreeMemory(span<u8> memory);
|
void FreeMemory(span<u8> memory);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows you to add/remove references to shared/transfer memory
|
* @brief Adds a reference to shared memory, extending its lifetime until `RemoveRef` is called
|
||||||
*/
|
*/
|
||||||
void AddRef(const std::shared_ptr<type::KMemory> &ptr);
|
void AddRef(std::shared_ptr<type::KMemory> ptr);
|
||||||
|
|
||||||
void RemoveRef(const std::shared_ptr<type::KMemory> &ptr);
|
/**
|
||||||
|
* @brief Removes the reference added by `AddRef`
|
||||||
|
*/
|
||||||
|
void RemoveRef(std::shared_ptr<type::KMemory> ptr);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return The cumulative size of all heap (Physical Memory + Process Heap) memory mappings, the code region and the main thread stack in bytes
|
* @return The cumulative size of all heap (Physical Memory + Process Heap) memory mappings, the code region and the main thread stack in bytes
|
||||||
@ -347,3 +355,16 @@ namespace skyline {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<> struct fmt::formatter<skyline::memory::Permission> {
|
||||||
|
template<typename ParseContext>
|
||||||
|
constexpr auto parse(ParseContext& ctx)
|
||||||
|
{
|
||||||
|
return ctx.begin();
|
||||||
|
}
|
||||||
|
template<typename FormatContext>
|
||||||
|
constexpr auto format(skyline::memory::Permission const& permission, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return fmt::format_to(ctx.out(), "{}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
namespace skyline::kernel::svc {
|
namespace skyline::kernel::svc {
|
||||||
void SetHeapSize(const DeviceState &state) {
|
void SetHeapSize(const DeviceState &state) {
|
||||||
u64 size{state.ctx->gpr.w1};
|
u32 size{state.ctx->gpr.w1};
|
||||||
|
|
||||||
if (!util::IsAligned(size, 0x200000)) [[unlikely]] {
|
if (!util::IsAligned(size, 0x200000)) [[unlikely]] {
|
||||||
state.ctx->gpr.w0 = result::InvalidSize;
|
state.ctx->gpr.w0 = result::InvalidSize;
|
||||||
@ -27,7 +27,7 @@ namespace skyline::kernel::svc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t heapCurrSize{state.process->memory.setHeapSize};
|
size_t heapCurrSize{state.process->memory.processHeapSize};
|
||||||
u8 *heapBaseAddr{state.process->memory.heap.data()};
|
u8 *heapBaseAddr{state.process->memory.heap.data()};
|
||||||
|
|
||||||
if (heapCurrSize < size)
|
if (heapCurrSize < size)
|
||||||
@ -35,7 +35,7 @@ namespace skyline::kernel::svc {
|
|||||||
else if (size < heapCurrSize)
|
else if (size < heapCurrSize)
|
||||||
state.process->memory.UnmapMemory(span<u8>{heapBaseAddr + size, heapCurrSize - size});
|
state.process->memory.UnmapMemory(span<u8>{heapBaseAddr + size, heapCurrSize - size});
|
||||||
|
|
||||||
state.process->memory.setHeapSize = size;
|
state.process->memory.processHeapSize = size;
|
||||||
|
|
||||||
state.ctx->gpr.w0 = Result{};
|
state.ctx->gpr.w0 = Result{};
|
||||||
state.ctx->gpr.x1 = reinterpret_cast<u64>(heapBaseAddr);
|
state.ctx->gpr.x1 = reinterpret_cast<u64>(heapBaseAddr);
|
||||||
@ -67,7 +67,7 @@ namespace skyline::kernel::svc {
|
|||||||
memory::Permission newPermission(static_cast<u8>(state.ctx->gpr.w2));
|
memory::Permission newPermission(static_cast<u8>(state.ctx->gpr.w2));
|
||||||
if ((!newPermission.r && newPermission.w) || newPermission.x) [[unlikely]] {
|
if ((!newPermission.r && newPermission.w) || newPermission.x) [[unlikely]] {
|
||||||
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
|
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
|
||||||
Logger::Warn("'permission' invalid: {}{}{}", newPermission.r ? 'R' : '-', newPermission.w ? 'W' : '-', newPermission.x ? 'X' : '-');
|
Logger::Warn("'permission' invalid: {}", newPermission);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ namespace skyline::kernel::svc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.process->memory.SetChunkPermission(span<u8>(address, size), newPermission);
|
state.process->memory.SetRegionPermission(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);
|
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{};
|
state.ctx->gpr.w0 = Result{};
|
||||||
@ -105,8 +105,8 @@ namespace skyline::kernel::svc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
memory::MemoryAttribute mask{.value = state.ctx->gpr.w2};
|
memory::MemoryAttribute mask{static_cast<u8>(state.ctx->gpr.w2)};
|
||||||
memory::MemoryAttribute value{.value = state.ctx->gpr.w3};
|
memory::MemoryAttribute value{static_cast<u8>(state.ctx->gpr.w3)};
|
||||||
|
|
||||||
auto maskedValue{mask.value | value.value};
|
auto maskedValue{mask.value | value.value};
|
||||||
if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) [[unlikely]] {
|
if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) [[unlikely]] {
|
||||||
@ -124,7 +124,7 @@ namespace skyline::kernel::svc {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
state.process->memory.SetCPUCachingOnChunks(span<u8>{address, size}, value.isUncached);
|
state.process->memory.SetRegionCpuCaching(span<u8>{address, size}, value.isUncached);
|
||||||
|
|
||||||
Logger::Debug("Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", static_cast<bool>(value.isUncached), address, address + size, size);
|
Logger::Debug("Set CPU caching to {} at 0x{:X} - 0x{:X} (0x{:X} bytes)", static_cast<bool>(value.isUncached), address, address + size, size);
|
||||||
state.ctx->gpr.w0 = Result{};
|
state.ctx->gpr.w0 = Result{};
|
||||||
@ -175,8 +175,8 @@ namespace skyline::kernel::svc {
|
|||||||
state.process->memory.MapStackMemory(span<u8>{destination, size});
|
state.process->memory.MapStackMemory(span<u8>{destination, size});
|
||||||
std::memcpy(destination, source, size);
|
std::memcpy(destination, source, size);
|
||||||
|
|
||||||
state.process->memory.SetChunkPermission(span<u8>{source, size}, {false, false, false});
|
state.process->memory.SetRegionPermission(span<u8>{source, size}, {false, false, false});
|
||||||
state.process->memory.SetLockOnChunks(span<u8>{source, size}, true);
|
state.process->memory.SetRegionBorrowed(span<u8>{source, size}, true);
|
||||||
|
|
||||||
Logger::Debug("Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
|
Logger::Debug("Mapped range 0x{:X} - 0x{:X} to 0x{:X} - 0x{:X} (Size: 0x{:X} bytes)", source, source + size, destination, destination + size, size);
|
||||||
state.ctx->gpr.w0 = Result{};
|
state.ctx->gpr.w0 = Result{};
|
||||||
@ -210,8 +210,8 @@ namespace skyline::kernel::svc {
|
|||||||
dstChunk = state.process->memory.GetChunk(dstChunk.first + dstChunk.second.size).value();
|
dstChunk = state.process->memory.GetChunk(dstChunk.first + dstChunk.second.size).value();
|
||||||
|
|
||||||
if ((destination + size) > dstChunk.first) [[likely]] {
|
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.SetRegionPermission(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);
|
state.process->memory.SetRegionBorrowed(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);
|
||||||
|
|
||||||
@ -239,7 +239,7 @@ namespace skyline::kernel::svc {
|
|||||||
.ipcRefCount = 0,
|
.ipcRefCount = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
Logger::Debug("Address: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Attributes: 0x{:X}, Permissions: {}{}{}", address, memInfo.address, memInfo.size, memInfo.type, memInfo.attributes, chunk->second.permission.r ? 'R' : '-', chunk->second.permission.w ? 'W' : '-', chunk->second.permission.x ? 'X' : '-');
|
Logger::Debug("Address: 0x{:X}, Region Start: 0x{:X}, Size: 0x{:X}, Type: 0x{:X}, Attributes: 0x{:X}, Permissions: {}", address, memInfo.address, memInfo.size, memInfo.type, memInfo.attributes, chunk->second.permission);
|
||||||
} else {
|
} else {
|
||||||
u64 addressSpaceEnd{reinterpret_cast<u64>(state.process->memory.addressSpace.end().base())};
|
u64 addressSpaceEnd{reinterpret_cast<u64>(state.process->memory.addressSpace.end().base())};
|
||||||
|
|
||||||
@ -254,7 +254,7 @@ namespace skyline::kernel::svc {
|
|||||||
|
|
||||||
*reinterpret_cast<memory::MemoryInfo *>(state.ctx->gpr.x0) = memInfo;
|
*reinterpret_cast<memory::MemoryInfo *>(state.ctx->gpr.x0) = memInfo;
|
||||||
// The page info, which is always 0
|
// The page info, which is always 0
|
||||||
state.ctx->gpr.x1 = 0;
|
state.ctx->gpr.w1 = 0;
|
||||||
|
|
||||||
state.ctx->gpr.w0 = Result{};
|
state.ctx->gpr.w0 = Result{};
|
||||||
}
|
}
|
||||||
@ -537,7 +537,7 @@ namespace skyline::kernel::svc {
|
|||||||
memory::Permission permission(static_cast<u8>(state.ctx->gpr.w3));
|
memory::Permission permission(static_cast<u8>(state.ctx->gpr.w3));
|
||||||
if ((!permission.r && !permission.w && !permission.x) || (permission.w && !permission.r) || permission.x) [[unlikely]] {
|
if ((!permission.r && !permission.w && !permission.x) || (permission.w && !permission.r) || permission.x) [[unlikely]] {
|
||||||
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
|
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
|
||||||
Logger::Warn("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
|
Logger::Warn("'permission' invalid: {}", permission);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,7 +613,7 @@ namespace skyline::kernel::svc {
|
|||||||
|
|
||||||
memory::Permission permission(static_cast<u8>(state.ctx->gpr.w3));
|
memory::Permission permission(static_cast<u8>(state.ctx->gpr.w3));
|
||||||
if ((permission.w && !permission.r) || permission.x) [[unlikely]] {
|
if ((permission.w && !permission.r) || permission.x) [[unlikely]] {
|
||||||
Logger::Warn("'permission' invalid: {}{}{}", permission.r ? 'R' : '-', permission.w ? 'W' : '-', permission.x ? 'X' : '-');
|
Logger::Warn("'permission' invalid: {}", permission);
|
||||||
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
|
state.ctx->gpr.w0 = result::InvalidNewMemoryPermission;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,12 @@
|
|||||||
#include "KProcess.h"
|
#include "KProcess.h"
|
||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
KMemory::KMemory(const DeviceState &state, KType objectType, size_t size) : KObject(state, objectType), guest() {
|
KMemory::KMemory(const DeviceState &state, KType objectType, size_t size) : KObject{state, objectType}, guest{} {
|
||||||
fd = ASharedMemory_create(objectType == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size);
|
fileDescriptor = ASharedMemory_create(objectType == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size);
|
||||||
if (fd < 0) [[unlikely]]
|
if (fileDescriptor < 0) [[unlikely]]
|
||||||
throw exception("An error occurred while creating shared memory: {}", fd);
|
throw exception("An error occurred while creating shared memory: {}", fileDescriptor);
|
||||||
|
|
||||||
u8 *hostPtr{static_cast<u8 *>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0))};
|
u8 *hostPtr{static_cast<u8 *>(mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fileDescriptor, 0))};
|
||||||
if (hostPtr == MAP_FAILED) [[unlikely]]
|
if (hostPtr == MAP_FAILED) [[unlikely]]
|
||||||
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
|
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
|
||||||
|
|
||||||
@ -28,7 +28,7 @@ namespace skyline::kernel::type {
|
|||||||
if (guest.valid()) [[unlikely]]
|
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());
|
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_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), fileDescriptor, 0) == MAP_FAILED) [[unlikely]]
|
||||||
throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno));
|
throw exception("An error occurred while mapping shared memory in guest: {}", strerror(errno));
|
||||||
guest = map;
|
guest = map;
|
||||||
|
|
||||||
@ -51,6 +51,6 @@ namespace skyline::kernel::type {
|
|||||||
if (host.valid())
|
if (host.valid())
|
||||||
munmap(host.data(), host.size());
|
munmap(host.data(), host.size());
|
||||||
|
|
||||||
close(fd);
|
close(fileDescriptor);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ namespace skyline::kernel::type {
|
|||||||
*/
|
*/
|
||||||
class KMemory : public KObject {
|
class KMemory : public KObject {
|
||||||
private:
|
private:
|
||||||
int fd; //!< A file descriptor to the underlying shared memory
|
int fileDescriptor; //!< A file descriptor to the underlying shared memory
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KMemory(const DeviceState &state, KType objectType, size_t size);
|
KMemory(const DeviceState &state, KType objectType, size_t size);
|
||||||
|
@ -50,7 +50,7 @@ namespace skyline::kernel::type {
|
|||||||
void KProcess::InitializeHeapTls() {
|
void KProcess::InitializeHeapTls() {
|
||||||
constexpr size_t DefaultHeapSize{0x200000};
|
constexpr size_t DefaultHeapSize{0x200000};
|
||||||
memory.MapHeapMemory(span<u8>{state.process->memory.heap.data(), DefaultHeapSize});
|
memory.MapHeapMemory(span<u8>{state.process->memory.heap.data(), DefaultHeapSize});
|
||||||
memory.setHeapSize = DefaultHeapSize;
|
memory.processHeapSize = DefaultHeapSize;
|
||||||
tlsExceptionContext = AllocateTlsSlot();
|
tlsExceptionContext = AllocateTlsSlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size)
|
KSharedMemory::KSharedMemory(const DeviceState &state, size_t size)
|
||||||
: KMemory(state, KType::KSharedMemory, size) {}
|
: KMemory{state, KType::KSharedMemory, size} {}
|
||||||
|
|
||||||
u8 *KSharedMemory::Map(span<u8> map, memory::Permission permission) {
|
u8 *KSharedMemory::Map(span<u8> map, memory::Permission permission) {
|
||||||
u8 *result{KMemory::Map(map, permission)};
|
u8 *result{KMemory::Map(map, permission)};
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace skyline::kernel::type {
|
namespace skyline::kernel::type {
|
||||||
KTransferMemory::KTransferMemory(const DeviceState &state, size_t size)
|
KTransferMemory::KTransferMemory(const DeviceState &state, size_t size)
|
||||||
: KMemory(state, KType::KTransferMemory, size) {}
|
: KMemory{state, KType::KTransferMemory, size} {}
|
||||||
|
|
||||||
u8 *KTransferMemory::Map(span<u8> map, memory::Permission permission) {
|
u8 *KTransferMemory::Map(span<u8> map, memory::Permission permission) {
|
||||||
std::memcpy(host.data(), map.data(), map.size());
|
std::memcpy(host.data(), map.data(), map.size());
|
||||||
@ -21,7 +21,7 @@ namespace skyline::kernel::type {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
} else {
|
} else {
|
||||||
state.process->memory.MapTransferMemory(guest, permission);
|
state.process->memory.MapTransferMemory(guest, permission);
|
||||||
state.process->memory.SetLockOnChunks(guest, true);
|
state.process->memory.SetRegionBorrowed(guest, true);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ namespace skyline::loader {
|
|||||||
|
|
||||||
if (process->memory.addressSpaceType == memory::AddressSpaceType::AddressSpace36Bit) {
|
if (process->memory.addressSpaceType == memory::AddressSpaceType::AddressSpace36Bit) {
|
||||||
process->memory.MapHeapMemory(span<u8>{base, patch.size + hookSize}); // ---
|
process->memory.MapHeapMemory(span<u8>{base, patch.size + hookSize}); // ---
|
||||||
process->memory.SetChunkPermission(span<u8>{base, patch.size + hookSize}, memory::Permission{false, false, false});
|
process->memory.SetRegionPermission(span<u8>{base, patch.size + hookSize}, memory::Permission{false, false, false});
|
||||||
} else {
|
} else {
|
||||||
process->memory.Reserve(span<u8>{base, patch.size + hookSize}); // ---
|
process->memory.Reserve(span<u8>{base, patch.size + hookSize}); // ---
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user