fix(libkernel): replace partly overlapped allocations with a fixed mapping
A fixed mapping without no-overwrite unmaps the registered pieces it overlaps before mapping, as it already did when the range lay inside registered allocations.
This commit is contained in:
parent
df16c4c256
commit
5cf013a299
|
|
@ -615,6 +615,30 @@ bool Reserved(const void* addr, size_t len) {
|
|||
return false;
|
||||
}
|
||||
|
||||
void UnmapRegistered(GuestAllocations::Mutation& mutation, void* addr, size_t len) {
|
||||
mutation.Unmap(addr, len, [&](const void* piece, std::size_t pieceBytes, const void* allocation, bool last) {
|
||||
auto* pieceAddress = const_cast<void*>(piece);
|
||||
std::lock_guard lock(g_directLock);
|
||||
EraseMappings(reinterpret_cast<std::uintptr_t>(piece), reinterpret_cast<std::uintptr_t>(piece) + pieceBytes);
|
||||
#if defined(__linux__)
|
||||
Unmap(pieceAddress, pieceBytes);
|
||||
#else
|
||||
if (KernelArena::Get().Contains(pieceAddress, pieceBytes)) munmap(pieceAddress, pieceBytes);
|
||||
else if (last) munmap_release(const_cast<void*>(allocation));
|
||||
else munmap(pieceAddress, pieceBytes);
|
||||
#endif
|
||||
});
|
||||
EraseReservations(addr, len);
|
||||
RecordProtection(addr, len, -1);
|
||||
}
|
||||
|
||||
void ReplaceFixedOverlap(GuestAllocations::Mutation& mutation, void* addr, size_t len, int flags) {
|
||||
constexpr int GuestMapNoOverwrite = 0x80;
|
||||
if (addr == nullptr || (flags & GuestMapFixedFlag) == 0) return;
|
||||
if ((flags & GuestMapNoOverwrite) == 0 && mutation.Overlaps(addr, len)) UnmapRegistered(mutation, addr, len);
|
||||
mutation.RequireAvailable(addr, len);
|
||||
}
|
||||
|
||||
bool FixedNoOverwriteConflict(const GuestAllocations::Mutation& mutation, void* addr, size_t len, int flags) {
|
||||
constexpr int GuestMapNoOverwrite = 0x80;
|
||||
if (addr == nullptr || (flags & GuestMapFixedFlag) == 0 || (flags & GuestMapNoOverwrite) == 0) return false;
|
||||
|
|
@ -634,7 +658,7 @@ int DoMapDirect(void** addr, size_t len, int prot, int flags, int64_t physStart,
|
|||
RecordProtection(*addr, len, prot);
|
||||
return 0;
|
||||
}
|
||||
if (*addr != nullptr && (flags & GuestMapFixedFlag) != 0) mutation.RequireAvailable(*addr, len);
|
||||
ReplaceFixedOverlap(mutation, *addr, len, flags);
|
||||
std::lock_guard lock(g_directLock);
|
||||
ValidatePhysicalRange(static_cast<std::uint64_t>(physStart), len);
|
||||
void* mapped = MapAligned(*addr, len, PROT_NONE, flags, alignment);
|
||||
|
|
@ -663,7 +687,7 @@ int DoMapAnon(void** addr, size_t len, int prot, int flags) {
|
|||
RecordProtection(*addr, len, prot);
|
||||
return 0;
|
||||
}
|
||||
if (*addr != nullptr && (flags & GuestMapFixedFlag) != 0) mutation.RequireAvailable(*addr, len);
|
||||
ReplaceFixedOverlap(mutation, *addr, len, flags);
|
||||
void* mapped = MapAligned(*addr, len, LinuxProtFromSce(prot), flags, PS5_PAGE_SIZE);
|
||||
try {
|
||||
mutation.Add(mapped, len, (prot & 3) != 0, (prot & 2) != 0);
|
||||
|
|
@ -738,20 +762,7 @@ int DoMunmap(void* addr, size_t len) {
|
|||
Trace("unmap %p+0x%zx", addr, len);
|
||||
if (len == 0 || (len & (PS5_PAGE_SIZE - 1)) != 0 || !addr) return SCE_KERNEL_ERROR_EINVAL;
|
||||
GuestAllocations::Mutation mutation;
|
||||
mutation.Unmap(addr, len, [&](const void* piece, std::size_t pieceBytes, const void* allocation, bool last) {
|
||||
auto* pieceAddress = const_cast<void*>(piece);
|
||||
std::lock_guard lock(g_directLock);
|
||||
EraseMappings(reinterpret_cast<std::uintptr_t>(piece), reinterpret_cast<std::uintptr_t>(piece) + pieceBytes);
|
||||
#if defined(__linux__)
|
||||
Unmap(pieceAddress, pieceBytes);
|
||||
#else
|
||||
if (KernelArena::Get().Contains(pieceAddress, pieceBytes)) munmap(pieceAddress, pieceBytes);
|
||||
else if (last) munmap_release(const_cast<void*>(allocation));
|
||||
else munmap(pieceAddress, pieceBytes);
|
||||
#endif
|
||||
});
|
||||
EraseReservations(addr, len);
|
||||
RecordProtection(addr, len, -1);
|
||||
UnmapRegistered(mutation, addr, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -280,6 +280,27 @@ static void CheckReleaseDirectMemoryClearsMappings() {
|
|||
Require(sceKernelMunmap(mapped, page * 2) == 0);
|
||||
}
|
||||
|
||||
static void CheckFixedMappingReplacesPartialOverlap() {
|
||||
constexpr std::size_t page = 0x4000;
|
||||
std::int64_t phys = 0;
|
||||
Require(sceKernelAllocateDirectMemory(0, 0x7fffffffffll, page * 3, 0, 0, &phys) == 0);
|
||||
void* reserved = nullptr;
|
||||
Require(sceKernelMapDirectMemory(&reserved, page * 3, 3, 0, phys, 0) == 0);
|
||||
Require(sceKernelMunmap(reserved, page * 3) == 0);
|
||||
void* head = reserved;
|
||||
Require(sceKernelMapDirectMemory(&head, page, 3, 0x10, phys, 0) == 0 && head == reserved);
|
||||
void* fixed = reserved;
|
||||
Require(sceKernelMapDirectMemory(&fixed, page * 3, 3, 0x10, phys, 0) == 0);
|
||||
Require(fixed == reserved);
|
||||
static_cast<unsigned char*>(fixed)[page * 2 + 7] = 0x5c;
|
||||
VirtualQueryInfo info{};
|
||||
Require(sceKernelVirtualQuery(fixed, 0, &info, sizeof(info)) == 0);
|
||||
Require(info.is_direct && info.offset == static_cast<std::uint64_t>(phys));
|
||||
Require(static_cast<unsigned char*>(fixed)[page * 2 + 7] == 0x5c);
|
||||
Require(sceKernelMunmap(fixed, page * 3) == 0);
|
||||
Require(sceKernelReleaseDirectMemory(phys, page * 3) == 0);
|
||||
}
|
||||
|
||||
static void CheckGetDirectMemoryType() {
|
||||
constexpr std::size_t page = 0x4000;
|
||||
std::int64_t first = 0;
|
||||
|
|
@ -1043,6 +1064,7 @@ int main() {
|
|||
CheckAudioCoprocessorProtection();
|
||||
CheckDirectMemoryFollowsPhysicalPages();
|
||||
CheckReleaseDirectMemoryClearsMappings();
|
||||
CheckFixedMappingReplacesPartialOverlap();
|
||||
CheckDirectMemoryGpuProtBits();
|
||||
CheckFixedVirtualReservation();
|
||||
CheckReservedRangeIsNotCommitted();
|
||||
|
|
|
|||
Loading…
Reference in New Issue