vm/savestates: Rewrite and fix stack_guarded

This commit is contained in:
Elad
2026-10-05 01:10:48 +03:00
parent a72c4c319b
commit 7b7983ca1a
2 changed files with 48 additions and 40 deletions
+47 -39
View File
@@ -1185,6 +1185,39 @@ namespace vm
bool block_t::try_alloc(u32 addr, u64 bflags, u32 size, std::shared_ptr<utils::shm>&& shm) const
{
if (!size)
{
return false;
}
if (this->flags & stack_guarded)
{
ensure(this->size >= 0x100000);
if (addr < this->addr + 4096 || addr + size - 1 >= this->addr + this->size - 4096 - 1)
{
return false;
}
// Protect guard pages
if (g_pages[addr / 4096 - 1] || g_pages[addr / 4096 + size / 4096])
{
return false;
}
// Guard pages must be consecutive for non-edge allocations
if (addr > this->addr + 4096 && g_pages[addr / 4096 - 2])
{
return false;
}
// Same
if (size + addr < this->addr + this->size - 4096 && g_pages[addr / 4096 + size / 4096 + 1])
{
return false;
}
}
// Check if memory area is already mapped
for (u32 i = addr / 4096; i <= (addr + size - 1) / 4096; i++)
{
@@ -1194,9 +1227,6 @@ namespace vm
}
}
const u32 page_addr = addr + (this->flags & stack_guarded ? 0x1000 : 0);
const u32 page_size = size - (this->flags & stack_guarded ? 0x2000 : 0);
// No flags are default to readable/writable
// Explicit (un...) flags are used to protect from such access
u8 flags = 0;
@@ -1225,15 +1255,8 @@ namespace vm
flags |= page_1m_size;
}
if (this->flags & stack_guarded)
{
// Mark overflow/underflow guard pages as allocated
ensure(!g_pages[addr / 4096].exchange(page_allocated));
ensure(!g_pages[addr / 4096 + size / 4096 - 1].exchange(page_allocated));
}
// Map "real" memory pages; provide a function to search for mirrors with private member access
_page_map(page_addr, flags, page_size, shm.get(), this->flags, [](vm::block_t* _this, utils::shm* shm)
_page_map(addr, flags, size, shm.get(), this->flags, [](vm::block_t* _this, utils::shm* shm)
{
auto& map = (_this->m.*block_map)();
@@ -1262,17 +1285,7 @@ namespace vm
{
auto fill64 = [](u8* ptr, u64 data, usz count)
{
#if defined(_M_X64) && defined(_MSC_VER)
__stosq(reinterpret_cast<u64*>(ptr), data, count);
#elif defined(ARCH_X64)
__asm__ ("mov %0, %%rdi; mov %1, %%rax; mov %2, %%rcx; rep stosq;"
:
: "r" (ptr), "r" (data), "r" (count)
: "rdi", "rax", "rcx", "memory");
#else
for (usz i = 0; i < count; i++)
reinterpret_cast<u64*>(ptr)[i] = data;
#endif
std::fill(ptr, ptr + count, data);
};
const u32 enda = addr + size - 4096;
@@ -1455,7 +1468,7 @@ namespace vm
const u32 min_page_size = flags & block_size_4k ? 0x1000 : 0x10000;
// Align to minimal page size
const u32 size = utils::align(orig_size, min_page_size) + (flags & stack_guarded ? 0x2000 : 0);
const u32 size = utils::align(orig_size, min_page_size);
// Check alignment (it's page allocation, so passing small values there is just silly)
if (align < min_page_size || align != (0x80000000u >> std::countl_zero(align)))
@@ -1503,7 +1516,7 @@ namespace vm
{
if (try_alloc(addr, flags, size, std::move(shm)))
{
return addr + (flags & stack_guarded ? 0x1000 : 0);
return addr;
}
if (addr == max)
@@ -1598,7 +1611,7 @@ namespace vm
vm::writer_lock lock;
const auto found = m_map.find(addr - (flags & stack_guarded ? 0x1000 : 0));
const auto found = m_map.find(addr);
if (found == m_map.end())
{
@@ -1611,14 +1624,7 @@ namespace vm
}
// Get allocation size
const auto size = found->second.first - (flags & stack_guarded ? 0x2000 : 0);
if (flags & stack_guarded)
{
// Clear guard pages
ensure(g_pages[addr / 4096 - 1].exchange(0) == page_allocated);
ensure(g_pages[addr / 4096 + size / 4096].exchange(0) == page_allocated);
}
const auto size = found->second.first;
// Unmap "real" memory pages
ensure(size == _page_unmap(addr, size, this->flags, found->second.second.get(), unmap_notification.event_data));
@@ -1684,7 +1690,7 @@ namespace vm
for (auto& entry : (m.*block_map)())
{
result += entry.second.first - (flags & stack_guarded ? 0x2000 : 0);
result += entry.second.first;
}
return result;
@@ -1834,7 +1840,7 @@ namespace vm
for (const auto& [addr, shm] : m_map)
{
// Assume first page flags represent all the map
ar(g_pages[addr / 4096 + !!(flags & stack_guarded)]);
ar(g_pages[addr / 4096]);
ar(addr);
ar(shm.first);
@@ -1851,8 +1857,7 @@ namespace vm
}
// Save raw binary image
const u32 guard_size = flags & stack_guarded ? 0x1000 : 0;
serialize_memory_bytes(ar, vm::get_super_ptr<u8>(addr + guard_size), shm.first - guard_size * 2);
serialize_memory_bytes(ar, vm::get_super_ptr<u8>(addr), shm.first);
}
else
{
@@ -1918,6 +1923,10 @@ namespace vm
{
pflags |= block_size_1m;
}
else
{
pflags |= block_size_4k;
}
// Map the memory through the same method as alloc() and falloc()
// Copy the shared handle unconditionally
@@ -1926,8 +1935,7 @@ namespace vm
if (flags & preallocated)
{
// Load binary image
const u32 guard_size = flags & stack_guarded ? 0x1000 : 0;
serialize_memory_bytes(ar, vm::get_super_ptr<u8>(addr0 + guard_size), size0 - guard_size * 2);
serialize_memory_bytes(ar, vm::get_super_ptr<u8>(addr0), size0);
}
}
}
+1 -1
View File
@@ -40,7 +40,7 @@ static std::array<serial_ver_t, 34> s_serial_versions;
return ::s_serial_versions[identifier].current_version;\
}
SERIALIZATION_VER(global_version, 0, 24) // For stuff not listed here
SERIALIZATION_VER(global_version, 0, 25) // For stuff not listed here
SERIALIZATION_VER(ppu, 1, 1, 2/*PPU sleep order*/, 3/*PPU FNID and module*/)
SERIALIZATION_VER(spu, 2, 1)
SERIALIZATION_VER(lv2_sync, 3, 1)