Store KMemory object ptrs in memory class to avoid linear-time unmap

This is quite a horrible solution but fixing it properly would require a whole rewrite of how we handle memory.
This commit is contained in:
Billy Laws 2022-10-30 16:26:08 +00:00
parent cc71e7b56c
commit 7f24c7b857
8 changed files with 38 additions and 21 deletions

View File

@ -210,6 +210,7 @@ namespace skyline::kernel {
lower->state = chunk.state; lower->state = chunk.state;
lower->permission = chunk.permission; lower->permission = chunk.permission;
lower->attributes = chunk.attributes; lower->attributes = chunk.attributes;
lower->memory = chunk.memory;
} else if (lower->ptr + lower->size > chunk.ptr + chunk.size) { } else if (lower->ptr + lower->size > chunk.ptr + chunk.size) {
auto lowerExtension{*lower}; auto lowerExtension{*lower};
lowerExtension.ptr = chunk.ptr + chunk.size; lowerExtension.ptr = chunk.ptr + chunk.size;

View File

@ -8,6 +8,10 @@
#include <common/file_descriptor.h> #include <common/file_descriptor.h>
namespace skyline { namespace skyline {
namespace kernel::type {
class KMemory;
}
namespace memory { namespace memory {
union Permission { union Permission {
/** /**
@ -195,9 +199,10 @@ namespace skyline {
memory::Permission permission; memory::Permission permission;
memory::MemoryState state; memory::MemoryState state;
memory::MemoryAttribute attributes; memory::MemoryAttribute attributes;
kernel::type::KMemory *memory{};
constexpr bool IsCompatible(const ChunkDescriptor &chunk) const { constexpr bool IsCompatible(const ChunkDescriptor &chunk) const {
return chunk.permission == permission && chunk.state.value == state.value && chunk.attributes.value == attributes.value; return chunk.permission == permission && chunk.state.value == state.value && chunk.attributes.value == attributes.value && chunk.memory == memory;
} }
}; };

View File

@ -1067,20 +1067,23 @@ namespace skyline::kernel::svc {
auto end{pointer + size}; auto end{pointer + size};
while (pointer < end) { while (pointer < end) {
auto memory{state.process->GetMemoryObject(pointer)}; auto chunk{state.process->memory.Get(pointer)};
if (memory) { if (chunk && chunk->memory) {
auto item{static_pointer_cast<type::KPrivateMemory>(memory->item)}; if (chunk->memory->objectType != type::KType::KPrivateMemory)
auto initialSize{item->guest.size()}; throw exception("Trying to unmap non-private memory");
if (item->memoryState == memory::states::Heap) {
if (item->guest.data() >= pointer) { auto memory{static_cast<type::KPrivateMemory *>(chunk->memory)};
if (item->guest.size() <= size) { auto initialSize{memory->guest.size()};
item->Resize(0); if (memory->memoryState == memory::states::Heap) {
if (memory->guest.data() >= pointer) {
if (memory->guest.size() <= size) {
memory->Resize(0);
state.process->CloseHandle(memory->handle); state.process->CloseHandle(memory->handle);
} else { } else {
item->Remap(span<u8>{pointer + size, static_cast<size_t>((pointer + item->guest.size() - item->guest.data())) - size}); memory->Remap(span<u8>{pointer + size, static_cast<size_t>((pointer + memory->guest.size() - memory->guest.data())) - size});
} }
} else if (item->guest.data() < pointer) { } else if (memory->guest.data() < pointer) {
item->Resize(static_cast<size_t>(pointer - item->guest.data())); memory->Resize(static_cast<size_t>(pointer - memory->guest.data()));
} }
} }
pointer += initialSize; pointer += initialSize;

View File

@ -8,14 +8,15 @@
#include "KProcess.h" #include "KProcess.h"
namespace skyline::kernel::type { namespace skyline::kernel::type {
KPrivateMemory::KPrivateMemory(const DeviceState &state, span<u8> guest, memory::Permission permission, memory::MemoryState memState) KPrivateMemory::KPrivateMemory(const DeviceState &state, KHandle handle, span<u8> guest, memory::Permission permission, memory::MemoryState memState)
: permission(permission), : permission(permission),
memoryState(memState), memoryState(memState),
handle(handle),
KMemory(state, KType::KPrivateMemory, guest) { KMemory(state, KType::KPrivateMemory, guest) {
if (!state.process->memory.base.contains(guest)) if (!state.process->memory.base.contains(guest))
throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", guest.data(), guest.data() + guest.size()); throw exception("KPrivateMemory allocation isn't inside guest address space: 0x{:X} - 0x{:X}", guest.data(), guest.data() + guest.size());
if (!util::IsPageAligned(guest.data()) || !util::IsPageAligned(guest.size())) if (!util::IsPageAligned(guest.data()) || !util::IsPageAligned(guest.size()))
throw exception("KPrivateMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", guest.data(), guest.data() + guest.size()); throw exception("KPrivateMemory mapping isn't page-aligned: 0x{:X} - 0x{:X} (0x{:X})", guest.data(), guest.data() + guest.size(), guest.size());
if (mprotect(guest.data(), guest.size(), PROT_READ | PROT_WRITE | PROT_EXEC) < 0) // We only need to reprotect as the allocation has already been reserved by the MemoryManager if (mprotect(guest.data(), guest.size(), PROT_READ | PROT_WRITE | PROT_EXEC) < 0) // We only need to reprotect as the allocation has already been reserved by the MemoryManager
throw exception("An occurred while mapping private memory: {} with 0x{:X} @ 0x{:X}", strerror(errno), guest.data(), guest.size()); throw exception("An occurred while mapping private memory: {} with 0x{:X} @ 0x{:X}", strerror(errno), guest.data(), guest.size());
@ -25,6 +26,7 @@ namespace skyline::kernel::type {
.size = guest.size(), .size = guest.size(),
.permission = permission, .permission = permission,
.state = memState, .state = memState,
.memory = this,
}); });
} }
@ -44,6 +46,7 @@ namespace skyline::kernel::type {
.size = nSize - guest.size(), .size = nSize - guest.size(),
.permission = permission, .permission = permission,
.state = memoryState, .state = memoryState,
.memory = this,
}); });
} }
guest = span<u8>{guest.data(), nSize}; guest = span<u8>{guest.data(), nSize};
@ -78,6 +81,7 @@ namespace skyline::kernel::type {
.size = size, .size = size,
.permission = pPermission, .permission = pPermission,
.state = memoryState, .state = memoryState,
.memory = this,
}); });
} }

View File

@ -14,12 +14,13 @@ namespace skyline::kernel::type {
public: public:
memory::Permission permission; memory::Permission permission;
memory::MemoryState memoryState; memory::MemoryState memoryState;
KHandle handle;
/** /**
* @param permission The permissions for the allocated memory (As reported to the application, host memory permissions aren't reflected by this) * @param permission The permissions for the allocated memory (As reported to the application, host memory permissions aren't reflected by this)
* @note 'ptr' needs to be in guest-reserved address space * @note 'ptr' needs to be in guest-reserved address space
*/ */
KPrivateMemory(const DeviceState &state, span<u8> guest, memory::Permission permission, memory::MemoryState memState); KPrivateMemory(const DeviceState &state, KHandle handle, span<u8> guest, memory::Permission permission, memory::MemoryState memState);
/** /**
* @note There is no check regarding if any expansions will cause the memory mapping to leak into other mappings * @note There is no check regarding if any expansions will cause the memory mapping to leak into other mappings

View File

@ -49,7 +49,7 @@ namespace skyline::kernel::type {
void KProcess::InitializeHeapTls() { void KProcess::InitializeHeapTls() {
constexpr size_t DefaultHeapSize{0x200000}; constexpr size_t DefaultHeapSize{0x200000};
heap = std::make_shared<KPrivateMemory>(state, span<u8>{state.process->memory.heap.data(), DefaultHeapSize}, memory::Permission{true, true, false}, memory::states::Heap); heap = std::make_shared<KPrivateMemory>(state, 0, span<u8>{state.process->memory.heap.data(), DefaultHeapSize}, memory::Permission{true, true, false}, memory::states::Heap);
InsertItem(heap); // Insert it into the handle table so GetMemoryObject will contain it InsertItem(heap); // Insert it into the handle table so GetMemoryObject will contain it
tlsExceptionContext = AllocateTlsSlot(); tlsExceptionContext = AllocateTlsSlot();
} }
@ -61,8 +61,8 @@ namespace skyline::kernel::type {
if ((slot = tlsPage->ReserveSlot())) if ((slot = tlsPage->ReserveSlot()))
return slot; return slot;
slot = tlsPages.empty() ? reinterpret_cast<u8 *>(memory.tlsIo.data()) : ((*(tlsPages.end() - 1))->memory->guest.data() + PAGE_SIZE); slot = tlsPages.empty() ? reinterpret_cast<u8 *>(memory.tlsIo.data()) : ((*(tlsPages.end() - 1))->memory->guest.data() + constant::PageSize);
auto tlsPage{std::make_shared<TlsPage>(std::make_shared<KPrivateMemory>(state, span<u8>{slot, PAGE_SIZE}, memory::Permission(true, true, false), memory::states::ThreadLocal))}; auto tlsPage{std::make_shared<TlsPage>(std::make_shared<KPrivateMemory>(state, 0, span<u8>{slot, constant::PageSize}, memory::Permission(true, true, false), memory::states::ThreadLocal))};
tlsPages.push_back(tlsPage); tlsPages.push_back(tlsPage);
return tlsPage->ReserveSlot(); return tlsPage->ReserveSlot();
} }
@ -72,7 +72,7 @@ namespace skyline::kernel::type {
if (disableThreadCreation) if (disableThreadCreation)
return nullptr; return nullptr;
if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process if (!stackTop && threads.empty()) { //!< Main thread stack is created by the kernel and owned by the process
mainThreadStack = std::make_shared<KPrivateMemory>(state, span<u8>{state.process->memory.stack.data(), state.process->npdm.meta.mainThreadStackSize}, memory::Permission{true, true, false}, memory::states::Stack); mainThreadStack = std::make_shared<KPrivateMemory>(state, 0, span<u8>{state.process->memory.stack.data(), state.process->npdm.meta.mainThreadStackSize}, memory::Permission{true, true, false}, memory::states::Stack);
stackTop = mainThreadStack->guest.end().base(); stackTop = mainThreadStack->guest.end().base();
} }
auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, stackTop, priority ? *priority : state.process->npdm.meta.mainThreadPriority, idealCore ? *idealCore : state.process->npdm.meta.idealCore).item}; auto thread{NewHandle<KThread>(this, threads.size(), entry, argument, stackTop, priority ? *priority : state.process->npdm.meta.mainThreadPriority, idealCore ? *idealCore : state.process->npdm.meta.idealCore).item};

View File

@ -117,7 +117,7 @@ namespace skyline {
std::unique_lock lock(handleMutex); std::unique_lock lock(handleMutex);
std::shared_ptr<objectClass> item; std::shared_ptr<objectClass> item;
if constexpr (std::is_same<objectClass, KThread>()) if constexpr (std::is_same<objectClass, KThread>() || std::is_same<objectClass, KPrivateMemory>())
item = std::make_shared<objectClass>(state, constant::BaseHandleIndex + handles.size(), args...); item = std::make_shared<objectClass>(state, constant::BaseHandleIndex + handles.size(), args...);
else else
item = std::make_shared<objectClass>(state, args...); item = std::make_shared<objectClass>(state, args...);

View File

@ -43,6 +43,7 @@ namespace skyline::kernel::type {
.attributes = memory::MemoryAttribute{ .attributes = memory::MemoryAttribute{
.isBorrowed = objectType == KType::KTransferMemory, .isBorrowed = objectType == KType::KTransferMemory,
}, },
.memory = this
}); });
return guest.data(); return guest.data();
@ -85,6 +86,7 @@ namespace skyline::kernel::type {
.attributes = memory::MemoryAttribute{ .attributes = memory::MemoryAttribute{
.isBorrowed = objectType == KType::KTransferMemory, .isBorrowed = objectType == KType::KTransferMemory,
}, },
.memory = this
}); });
} }
} }
@ -118,7 +120,8 @@ namespace skyline::kernel::type {
.state = memoryState, .state = memoryState,
.attributes = memory::MemoryAttribute{ .attributes = memory::MemoryAttribute{
.isBorrowed = false, .isBorrowed = false,
} },
.memory = this
}); });
} }
} }