General memman fixups & address review

This commit is contained in:
TheASVigilante 2023-05-05 18:15:47 +02:00
parent 1513e5f6df
commit 850f616dc7
9 changed files with 202 additions and 218 deletions

View File

@ -7,46 +7,36 @@
#include "types/KProcess.h"
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 {
if (base.valid() && !base.empty())
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) {
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) {
void MemoryManager::MapInternal(const std::pair<u8 *, ChunkDescriptor> &newDesc) {
// The chunk that contains / precedes the new chunk base address
auto firstChunkBase{upper_bound(newDesc->first)};
while (newDesc->first <= firstChunkBase->first)
auto firstChunkBase{chunks.lower_bound(newDesc.first)};
if (newDesc.first <= firstChunkBase->first)
--firstChunkBase;
// The chunk that contains / follows the end address of the new chunk
auto lastChunkBase{upper_bound(newDesc->first + newDesc->second.size)};
while ((newDesc->first + newDesc->second.size) < lastChunkBase->first)
auto lastChunkBase{chunks.lower_bound(newDesc.first + newDesc.second.size)};
if ((newDesc.first + newDesc.second.size) < lastChunkBase->first)
--lastChunkBase;
ChunkDescriptor firstChunk{firstChunkBase->second};
ChunkDescriptor lastChunk{lastChunkBase->second};
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
if (firstChunkBase->first == lastChunkBase->first) {
if (firstChunk.IsCompatible(newDesc->second)) [[unlikely]]
if (firstChunk.IsCompatible(newDesc.second)) [[unlikely]]
// No editing necessary
return;
@ -54,15 +44,15 @@ namespace skyline::kernel {
needsReprotection = true;
// 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;
// We create the chunk's second half
lastChunk.size = static_cast<size_t>((lastChunkBase->first + lastChunk.size) - (newDesc->first + newDesc->second.size));
chunks[newDesc->first + newDesc->second.size] = lastChunk;
lastChunk.size = static_cast<size_t>((lastChunkBase->first + lastChunk.size) - (newDesc.first + newDesc.second.size));
chunks.insert({newDesc.first + newDesc.second.size, lastChunk});
// Insert new chunk in between
chunks[newDesc->first] = newDesc->second;
chunks.insert(newDesc);
} else {
// If there are descriptors between first and last chunk, delete them
if ((firstChunkBase->first + firstChunk.size) != lastChunkBase->first) {
@ -79,13 +69,13 @@ namespace skyline::kernel {
bool shouldInsert{true};
if (firstChunk.IsCompatible(newDesc->second)) {
if (firstChunk.IsCompatible(newDesc.second)) {
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;
} else if ((firstChunkBase->first + firstChunk.size) != newDesc->first) {
firstChunk.size = static_cast<size_t>(newDesc->first - firstChunkBase->first);
} else if ((firstChunkBase->first + firstChunk.size) != newDesc.first) {
firstChunk.size = static_cast<size_t>(newDesc.first - firstChunkBase->first);
chunks[firstChunkBase->first] = firstChunk;
@ -93,25 +83,25 @@ namespace skyline::kernel {
needsReprotection = true;
}
if (lastChunk.IsCompatible(newDesc->second)) {
if (lastChunk.IsCompatible(newDesc.second)) {
u8 *oldBase{lastChunkBase->first};
chunks.erase(lastChunkBase);
if (shouldInsert) {
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 {
firstChunk.size = static_cast<size_t>((lastChunk.size + oldBase) - firstChunkBase->first);
chunks[firstChunkBase->first] = firstChunk;
}
} else if ((newDesc->first + newDesc->second.size) != lastChunkBase->first) {
lastChunk.size = static_cast<size_t>((lastChunk.size + lastChunkBase->first) - (newDesc->first + newDesc->second.size));
} else if ((newDesc.first + newDesc.second.size) != lastChunkBase->first) {
lastChunk.size = static_cast<size_t>((lastChunk.size + lastChunkBase->first) - (newDesc.first + newDesc.second.size));
chunks.erase(lastChunkBase);
chunks[newDesc->first + newDesc->second.size] = lastChunk;
chunks[newDesc.first + newDesc.second.size] = lastChunk;
if ((lastChunk.state == memory::states::Unmapped) != isUnmapping)
needsReprotection = true;
@ -119,49 +109,45 @@ namespace skyline::kernel {
// Insert if not merged
if (shouldInsert)
chunks[newDesc->first] = newDesc->second;
chunks.insert(newDesc);
}
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));
}
void MemoryManager::ForeachChunkinRange(span<skyline::u8> memory, auto editCallback) {
auto chunkBase{upper_bound(memory.data())};
void MemoryManager::ForeachChunkinRange(span<u8> memory, auto editCallback) {
auto chunkBase{chunks.lower_bound(memory.data())};
if (memory.data() < chunkBase->first)
--chunkBase;
ChunkDescriptor resultChunk{chunkBase->second};
size_t sizeLeft{memory.size()};
if (chunkBase->first < memory.data()) {
size_t copySize{std::min<size_t>(resultChunk.size - (static_cast<size_t>(memory.data() - chunkBase->first)), memory.size())};
if (chunkBase->first < memory.data()) [[unlikely]] {
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);
temp.second.size = copySize;
std::pair<u8 *, ChunkDescriptor> temp{memory.data(), chunkBase->second};
temp.second.size = chunkSize;
editCallback(temp);
++chunkBase;
resultChunk = chunkBase->second;
sizeLeft -= copySize;
sizeLeft -= chunkSize;
}
while (sizeLeft) {
if (sizeLeft < resultChunk.size) {
std::pair<u8 *, ChunkDescriptor> temp(chunkBase->first, resultChunk);
if (sizeLeft < chunkBase->second.size) {
std::pair<u8 *, ChunkDescriptor> temp(*chunkBase);
temp.second.size = sizeLeft;
editCallback(temp);
break;
} else [[likely]] {
std::pair<u8 *, ChunkDescriptor> temp(chunkBase->first, resultChunk);
std::pair<u8 *, ChunkDescriptor> temp(*chunkBase);
editCallback(temp);
sizeLeft = sizeLeft - resultChunk.size;
sizeLeft = sizeLeft - chunkBase->second.size;
++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
static constexpr size_t KgslReservedRegionSize{1ULL << 35};
base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false);
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;
} else {
codeBase36Bit = AllocateMappedRange(0x78000000, RegionAlignment, 0x8000000, KgslReservedRegionSize, false);
base = AllocateMappedRange(baseSize, RegionAlignment, KgslReservedRegionSize, addressSpace.size(), false);
code = codeBase36Bit = AllocateMappedRange(0x78000000, RegionAlignment, 0x8000000, KgslReservedRegionSize, false);
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};
}
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) {
@ -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
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),
.state = memory::states::Heap,
});
MapInternal(&tmp);
.state = memory::states::Heap
}));
}
// 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]]
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());
break;
@ -364,166 +344,151 @@ namespace skyline::kernel {
return span<u8>{reinterpret_cast<u8 *>(mirrorBase), totalSize};
}
void MemoryManager::SetLockOnChunks(span<u8> memory, bool value) {
std::unique_lock lock(mutex);
void MemoryManager::SetRegionBorrowed(span<u8> memory, bool value) {
std::unique_lock lock{mutex};
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
desc.second.attributes.isBorrowed = value;
MapInternal(&desc);
MapInternal(desc);
});
}
void MemoryManager::SetCPUCachingOnChunks(span<u8> memory, bool value) {
std::unique_lock lock(mutex);
void MemoryManager::SetRegionCpuCaching(span<u8> memory, bool value) {
std::unique_lock lock{mutex};
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
desc.second.attributes.isUncached = value;
MapInternal(&desc);
MapInternal(desc);
});
}
void MemoryManager::SetChunkPermission(span<u8> memory, memory::Permission permission) {
std::unique_lock lock(mutex);
void MemoryManager::SetRegionPermission(span<u8> memory, memory::Permission permission) {
std::unique_lock lock{mutex};
ForeachChunkinRange(memory, [&](std::pair<u8 *, ChunkDescriptor> &desc) __attribute__((always_inline)) {
desc.second.permission = permission;
MapInternal(&desc);
MapInternal(desc);
});
}
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]]
return std::nullopt;
auto chunkBase = upper_bound(addr);
if (addr < chunkBase->first) [[likely]]
auto chunkBase = chunks.lower_bound(addr);
if (addr < chunkBase->first)
--chunkBase;
return std::make_optional(*chunkBase);
}
__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(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = permission,
.state = memory::states::Code});
MapInternal(&temp);
.state = memory::states::Code
}));
}
__attribute__((always_inline)) void MemoryManager::MapMutableCodeMemory(span<u8> memory) {
std::unique_lock lock(mutex);
std::unique_lock lock{mutex};
std::pair<u8 *, ChunkDescriptor> temp(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = {true, true, false},
.state = memory::states::CodeMutable});
MapInternal(&temp);
.state = memory::states::CodeMutable
}));
}
__attribute__((always_inline)) void MemoryManager::MapStackMemory(span<u8> memory) {
std::unique_lock lock(mutex);
std::unique_lock lock{mutex};
std::pair<u8 *, ChunkDescriptor> temp(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = {true, true, false},
.state = memory::states::Stack,
.isSrcMergeDisallowed = true});
MapInternal(&temp);
.isSrcMergeDisallowed = true
}));
}
__attribute__((always_inline)) void MemoryManager::MapHeapMemory(span<u8> memory) {
std::unique_lock lock(mutex);
std::unique_lock lock{mutex};
std::pair<u8 *, ChunkDescriptor> temp(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = {true, true, false},
.state = memory::states::Heap});
MapInternal(&temp);
.state = memory::states::Heap
}));
}
__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(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = permission,
.state = memory::states::SharedMemory,
.isSrcMergeDisallowed = true});
MapInternal(&temp);
.isSrcMergeDisallowed = true
}));
}
__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(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = permission,
.state = permission.raw ? memory::states::TransferMemory : memory::states::TransferMemoryIsolated,
.isSrcMergeDisallowed = true});
MapInternal(&temp);
.isSrcMergeDisallowed = true
}));
}
__attribute__((always_inline)) void MemoryManager::MapThreadLocalMemory(span<u8> memory) {
std::unique_lock lock(mutex);
std::unique_lock lock{mutex};
std::pair<u8 *, ChunkDescriptor> temp(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = {true, true, false},
.state = memory::states::ThreadLocal});
MapInternal(&temp);
.state = memory::states::ThreadLocal
}));
}
__attribute__((always_inline)) void MemoryManager::Reserve(span<u8> memory) {
std::unique_lock lock(mutex);
std::unique_lock lock{mutex};
std::pair<u8 *, ChunkDescriptor> temp(
memory.data(),
ChunkDescriptor{
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = {false, false, false},
.state = memory::states::Reserved});
MapInternal(&temp);
.state = memory::states::Reserved
}));
}
__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)
FreeMemory(span<u8>((u8 *)desc.first, desc.second.size));
FreeMemory(span<u8>(desc.first, desc.second.size));
});
std::pair<u8 *, ChunkDescriptor> temp(
memory.data(),
ChunkDescriptor{
.size = memory.size(),
.permission = {false, false, false},
.state = memory::states::Unmapped});
MapInternal(&temp);
MapInternal(std::pair<u8 *, ChunkDescriptor>(
memory.data(),{
.size = memory.size(),
.permission = {false, false, false},
.state = memory::states::Unmapped
}));
}
__attribute__((always_inline)) void MemoryManager::FreeMemory(span<u8> memory) {
@ -535,25 +500,23 @@ namespace skyline::kernel {
Logger::Error("Failed to free memory: {}", strerror(errno));
}
void MemoryManager::AddRef(const std::shared_ptr<type::KMemory> &ptr) {
memRefs.push_back(ptr);
void MemoryManager::AddRef(std::shared_ptr<type::KMemory> ptr) {
memRefs.push_back(std::move(ptr));
}
void MemoryManager::RemoveRef(const std::shared_ptr<type::KMemory> &ptr) {
std::vector<std::shared_ptr<type::KMemory>>::iterator i{std::find(memRefs.begin(), memRefs.end(), ptr)};
void MemoryManager::RemoveRef(std::shared_ptr<type::KMemory> ptr) {
auto i = std::find(memRefs.begin(), memRefs.end(), ptr);
if (*i == ptr) {
if (*i == ptr) [[likely]]
memRefs.erase(i);
}
}
size_t MemoryManager::GetUserMemoryUsage() {
std::shared_lock lock(mutex);
std::shared_lock lock{mutex};
size_t size{};
auto currChunk = upper_bound(heap.data());
if (heap.data() < currChunk->first) [[likely]]
--currChunk;
auto currChunk = chunks.lower_bound(heap.data());
while (currChunk->first < heap.end().base()) {
if (currChunk->second.state == memory::states::Heap)
size += currChunk->second.size;
@ -564,7 +527,7 @@ namespace skyline::kernel {
}
size_t MemoryManager::GetSystemResourceUsage() {
std::shared_lock lock(mutex);
std::shared_lock lock{mutex};
constexpr size_t KMemoryBlockSize{0x40};
return std::min(static_cast<size_t>(state.process->npdm.meta.systemResourceSize), util::AlignUp(chunks.size() * KMemoryBlockSize, constant::PageSize));
}

View File

@ -18,19 +18,19 @@ namespace skyline {
/**
* @brief Initializes all permissions to false
*/
constexpr Permission() : raw() {}
constexpr Permission() : raw{} {}
/**
* @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 write If memory has write 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; }
@ -63,13 +63,18 @@ namespace skyline {
* @url https://switchbrew.org/wiki/SVC#MemoryAttribute
*/
union MemoryAttribute {
constexpr MemoryAttribute() : value{} {}
constexpr explicit MemoryAttribute(u8 value) : value{value} {}
struct {
bool isBorrowed : 1; //!< This is required for async IPC user buffers
bool isIpcLocked : 1; //!< True when IpcRefCount > 0
bool isDeviceShared : 1; //!< True when DeviceRefCount > 0
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
*/
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 {
return value == other.value;
@ -156,7 +161,7 @@ namespace skyline {
bool codeMemoryAllowed : 1; //!< If the application can use svcCreateCodeMemory on this block
bool isLinearMapped : 1; //!< If this block is mapped linearly
};
u32 value{};
u32 value;
};
static_assert(sizeof(MemoryState) == sizeof(u32));
@ -202,10 +207,10 @@ namespace skyline {
namespace kernel {
struct ChunkDescriptor {
bool isSrcMergeDisallowed;
size_t size;
memory::Permission permission;
memory::MemoryState state;
memory::MemoryAttribute attributes;
memory::MemoryState state;
size_t size;
constexpr bool IsCompatible(const ChunkDescriptor &chunk) const noexcept {
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;
// Workaround for broken std implementation
std::map<u8 *, ChunkDescriptor>::iterator upper_bound(u8 *address);
void MapInternal(std::pair<u8 *, ChunkDescriptor> *newDesc);
void MapInternal(const std::pair<u8 *, ChunkDescriptor> &newDesc);
void ForeachChunkinRange(span<u8> memory, auto editCallback);
@ -240,7 +242,7 @@ namespace skyline {
span<u8> stack{};
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
@ -272,27 +274,27 @@ namespace skyline {
span<u8> CreateMirrors(const std::vector<span<u8>> &regions);
/**
* @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 SetCPUCachingOnChunks(span<u8> memory, bool value);
void SetRegionBorrowed(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
*/
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
*/
std::optional<std::pair<u8 *, ChunkDescriptor>> GetChunk(u8 *addr);
/**
* Various mapping functions for use by the guest
* @note UnmapMemory frees the underlying memory as well
*/
// Various mapping functions for use by the guest
void MapCodeMemory(span<u8> memory, memory::Permission permission);
void MapMutableCodeMemory(span<u8> memory);
@ -309,6 +311,9 @@ namespace skyline {
void Reserve(span<u8> memory);
/**
* @note `UnmapMemory` also calls `FreeMemory` on the unmapped memory range
*/
void UnmapMemory(span<u8> memory);
/**
@ -318,11 +323,14 @@ namespace skyline {
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
@ -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' : '-');
}
};

View File

@ -11,7 +11,7 @@
namespace skyline::kernel::svc {
void SetHeapSize(const DeviceState &state) {
u64 size{state.ctx->gpr.w1};
u32 size{state.ctx->gpr.w1};
if (!util::IsAligned(size, 0x200000)) [[unlikely]] {
state.ctx->gpr.w0 = result::InvalidSize;
@ -27,7 +27,7 @@ namespace skyline::kernel::svc {
return;
}
size_t heapCurrSize{state.process->memory.setHeapSize};
size_t heapCurrSize{state.process->memory.processHeapSize};
u8 *heapBaseAddr{state.process->memory.heap.data()};
if (heapCurrSize < size)
@ -35,7 +35,7 @@ namespace skyline::kernel::svc {
else if (size < heapCurrSize)
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.x1 = reinterpret_cast<u64>(heapBaseAddr);
@ -67,7 +67,7 @@ namespace skyline::kernel::svc {
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' : '-');
Logger::Warn("'permission' invalid: {}", newPermission);
return;
}
@ -78,7 +78,7 @@ namespace skyline::kernel::svc {
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);
state.ctx->gpr.w0 = Result{};
@ -105,8 +105,8 @@ namespace skyline::kernel::svc {
return;
}
memory::MemoryAttribute mask{.value = state.ctx->gpr.w2};
memory::MemoryAttribute value{.value = state.ctx->gpr.w3};
memory::MemoryAttribute mask{static_cast<u8>(state.ctx->gpr.w2)};
memory::MemoryAttribute value{static_cast<u8>(state.ctx->gpr.w3)};
auto maskedValue{mask.value | value.value};
if (maskedValue != mask.value || !mask.isUncached || mask.isDeviceShared || mask.isBorrowed || mask.isIpcLocked) [[unlikely]] {
@ -124,7 +124,7 @@ namespace skyline::kernel::svc {
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);
state.ctx->gpr.w0 = Result{};
@ -175,8 +175,8 @@ namespace skyline::kernel::svc {
state.process->memory.MapStackMemory(span<u8>{destination, size});
std::memcpy(destination, source, size);
state.process->memory.SetChunkPermission(span<u8>{source, size}, {false, false, false});
state.process->memory.SetLockOnChunks(span<u8>{source, size}, true);
state.process->memory.SetRegionPermission(span<u8>{source, size}, {false, false, false});
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);
state.ctx->gpr.w0 = Result{};
@ -210,8 +210,8 @@ namespace skyline::kernel::svc {
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);
state.process->memory.SetRegionPermission(span<u8>{source + (dstChunk.first - destination), dstChunk.second.size}, dstChunk.second.permission);
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);
@ -239,7 +239,7 @@ namespace skyline::kernel::svc {
.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 {
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;
// The page info, which is always 0
state.ctx->gpr.x1 = 0;
state.ctx->gpr.w1 = 0;
state.ctx->gpr.w0 = Result{};
}
@ -537,7 +537,7 @@ namespace skyline::kernel::svc {
memory::Permission permission(static_cast<u8>(state.ctx->gpr.w3));
if ((!permission.r && !permission.w && !permission.x) || (permission.w && !permission.r) || permission.x) [[unlikely]] {
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;
}
@ -613,7 +613,7 @@ namespace skyline::kernel::svc {
memory::Permission permission(static_cast<u8>(state.ctx->gpr.w3));
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;
return;
}

View File

@ -8,12 +8,12 @@
#include "KProcess.h"
namespace skyline::kernel::type {
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);
if (fd < 0) [[unlikely]]
throw exception("An error occurred while creating shared memory: {}", fd);
KMemory::KMemory(const DeviceState &state, KType objectType, size_t size) : KObject{state, objectType}, guest{} {
fileDescriptor = ASharedMemory_create(objectType == KType::KSharedMemory ? "HOS-KSharedMemory" : "HOS-KTransferMemory", size);
if (fileDescriptor < 0) [[unlikely]]
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]]
throw exception("An occurred while mapping shared memory: {}", strerror(errno));
@ -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_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));
guest = map;
@ -51,6 +51,6 @@ namespace skyline::kernel::type {
if (host.valid())
munmap(host.data(), host.size());
close(fd);
close(fileDescriptor);
}
}

View File

@ -12,7 +12,7 @@ namespace skyline::kernel::type {
*/
class KMemory : public KObject {
private:
int fd; //!< A file descriptor to the underlying shared memory
int fileDescriptor; //!< A file descriptor to the underlying shared memory
public:
KMemory(const DeviceState &state, KType objectType, size_t size);

View File

@ -50,7 +50,7 @@ namespace skyline::kernel::type {
void KProcess::InitializeHeapTls() {
constexpr size_t DefaultHeapSize{0x200000};
memory.MapHeapMemory(span<u8>{state.process->memory.heap.data(), DefaultHeapSize});
memory.setHeapSize = DefaultHeapSize;
memory.processHeapSize = DefaultHeapSize;
tlsExceptionContext = AllocateTlsSlot();
}

View File

@ -6,7 +6,7 @@
namespace skyline::kernel::type {
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 *result{KMemory::Map(map, permission)};

View File

@ -6,7 +6,7 @@
namespace skyline::kernel::type {
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) {
std::memcpy(host.data(), map.data(), map.size());
@ -21,7 +21,7 @@ namespace skyline::kernel::type {
return nullptr;
} else {
state.process->memory.MapTransferMemory(guest, permission);
state.process->memory.SetLockOnChunks(guest, true);
state.process->memory.SetRegionBorrowed(guest, true);
return result;
}
}

View File

@ -91,7 +91,7 @@ namespace skyline::loader {
if (process->memory.addressSpaceType == memory::AddressSpaceType::AddressSpace36Bit) {
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 {
process->memory.Reserve(span<u8>{base, patch.size + hookSize}); // ---
}