vm: allow 4k-aligned allocations for vm::stack

Fix utils::shm::map logic for MapViewOfFileEx
This commit is contained in:
Nekotekina 2018-08-27 17:52:47 +03:00
parent c49c7de427
commit 85fa0942e7
4 changed files with 117 additions and 37 deletions

View File

@ -167,17 +167,35 @@ namespace utils
u8* shm::map(void* ptr, protection prot) const
{
#ifdef _WIN32
DWORD access = 0;
DWORD access = FILE_MAP_WRITE;
switch (prot)
{
case protection::rw: access = FILE_MAP_WRITE; break;
case protection::ro: access = FILE_MAP_READ; break;
case protection::no: break;
case protection::wx: access = FILE_MAP_WRITE | FILE_MAP_EXECUTE; break;
case protection::rx: access = FILE_MAP_READ | FILE_MAP_EXECUTE; break;
case protection::rw:
case protection::ro:
case protection::no:
break;
case protection::wx:
case protection::rx:
access |= FILE_MAP_EXECUTE;
break;
}
return static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr));
if (auto ret = static_cast<u8*>(::MapViewOfFileEx(m_handle, access, 0, 0, m_size, ptr)))
{
if (prot != protection::rw && prot != protection::wx)
{
DWORD old;
if (!::VirtualProtect(ret, m_size, +prot, &old))
{
::UnmapViewOfFile(ret);
return nullptr;
}
}
return ret;
}
return nullptr;
#else
return static_cast<u8*>(::mmap((void*)((u64)ptr & -0x10000), m_size, +prot, MAP_SHARED | (ptr ? MAP_FIXED : 0), m_file, 0));
#endif

View File

@ -348,7 +348,7 @@ void ppu_thread::on_init(const std::shared_ptr<void>& _this)
if (!stack_addr)
{
// Allocate stack + gap between stacks
auto new_stack_base = vm::alloc(stack_size + 4096, vm::stack);
auto new_stack_base = vm::alloc(stack_size + 4096, vm::stack, 4096);
if (!new_stack_base)
{
fmt::throw_exception("Out of stack memory (size=0x%x)" HERE, stack_size);

View File

@ -276,10 +276,8 @@ namespace vm
// Memory pages
std::array<memory_page, 0x100000000 / 4096> g_pages{};
static void _page_map(u32 addr, u8 flags, utils::shm& shm)
static void _page_map(u32 addr, u8 flags, u32 size, utils::shm* shm)
{
const u32 size = shm.size();
if (!size || (size | addr) % 4096 || flags & page_allocated)
{
fmt::throw_exception("Invalid arguments (addr=0x%x, size=0x%x)" HERE, addr, size);
@ -293,7 +291,12 @@ namespace vm
}
}
if (shm.map_critical(g_base_addr + addr) != g_base_addr + addr)
if (!shm)
{
utils::memory_protect(g_base_addr + addr, size, utils::protection::rw);
std::memset(g_base_addr + addr, 0, size);
}
else if (shm->map_critical(g_base_addr + addr) != g_base_addr + addr)
{
fmt::throw_exception("Memory mapping failed - blame Windows (addr=0x%x, size=0x%x, flags=0x%x)", addr, size, flags);
}
@ -375,28 +378,30 @@ namespace vm
return true;
}
static void _page_unmap(u32 addr, utils::shm& shm)
static u32 _page_unmap(u32 addr, u32 max_size, utils::shm* shm)
{
const u32 size = shm.size();
if (!size || (size | addr) % 4096)
if (!max_size || (max_size | addr) % 4096)
{
fmt::throw_exception("Invalid arguments (addr=0x%x, size=0x%x)" HERE, addr, size);
fmt::throw_exception("Invalid arguments (addr=0x%x, max_size=0x%x)" HERE, addr, max_size);
}
// Determine deallocation size
u32 size = 0;
bool is_exec = false;
for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++)
for (u32 i = addr / 4096; i < addr / 4096 + max_size / 4096; i++)
{
if ((g_pages[i].flags & page_allocated) == 0)
{
fmt::throw_exception("Memory not mapped (addr=0x%x, size=0x%x, current_addr=0x%x)" HERE, addr, size, i * 4096);
break;
}
if (g_pages[i].flags & page_executable)
{
is_exec = true;
}
size += 4096;
}
for (u32 i = addr / 4096; i < addr / 4096 + size / 4096; i++)
@ -407,7 +412,14 @@ namespace vm
}
}
shm.unmap_critical(g_base_addr + addr);
if (!shm)
{
utils::memory_protect(g_base_addr + addr, size, utils::protection::no);
}
else
{
shm->unmap_critical(g_base_addr + addr);
}
if (is_exec)
{
@ -418,6 +430,8 @@ namespace vm
{
utils::memory_decommit(g_stat_addr + addr, size);
}
return size;
}
bool check_addr(u32 addr, u32 size, u8 flags)
@ -486,10 +500,8 @@ namespace vm
}
}
bool block_t::try_alloc(u32 addr, u8 flags, std::shared_ptr<utils::shm>&& shm)
bool block_t::try_alloc(u32 addr, u8 flags, u32 size, std::shared_ptr<utils::shm>&& shm)
{
const u32 size = shm->size();
// Check if memory area is already mapped
for (u32 i = addr / 4096; i <= (addr + size - 1) / 4096; i++)
{
@ -500,7 +512,7 @@ namespace vm
}
// Map "real" memory pages
_page_map(addr, flags, *shm);
_page_map(addr, flags, size, shm.get());
// Add entry
m_map[addr] = std::move(shm);
@ -539,6 +551,13 @@ namespace vm
utils::memory_commit(g_reservations + 0xfff0000, 0x10000);
utils::memory_commit(g_reservations2 + 0xfff0000, 0x10000);
}
if (flags & 0x100)
{
// Special path for 4k-aligned pages
m_common = std::make_shared<utils::shm>(size);
verify(HERE), m_common->map_critical(vm::base(addr), utils::protection::no) == vm::base(addr);
}
}
block_t::~block_t()
@ -547,9 +566,18 @@ namespace vm
vm::writer_lock lock(0);
// Deallocate all memory
for (auto& entry : m_map)
for (auto it = m_map.begin(), end = m_map.end(); it != end;)
{
_page_unmap(entry.first, *entry.second);
const auto next = std::next(it);
const auto size = (next == end ? this->addr + this->size : next->first) - it->first;
_page_unmap(it->first, size, it->second.get());
it = next;
}
// Special path for 4k-aligned pages
if (m_common)
{
m_common->unmap_critical(vm::base(addr));
}
}
@ -564,11 +592,14 @@ namespace vm
{
vm::writer_lock lock(0);
// Determine minimal alignment
const u32 min_page_size = flags & 0x100 ? 0x1000 : 0x10000;
// Align to minimal page size
const u32 size = ::align(orig_size, 0x10000);
const u32 size = ::align(orig_size, min_page_size);
// Check alignment (it's page allocation, so passing small values there is just silly)
if (align < 0x10000 || align != (0x80000000u >> cntlz32(align, true)))
if (align < min_page_size || align != (0x80000000u >> cntlz32(align, true)))
{
fmt::throw_exception("Invalid alignment (size=0x%x, align=0x%x)" HERE, size, align);
}
@ -591,12 +622,19 @@ namespace vm
}
// Create or import shared memory object
std::shared_ptr<utils::shm> shm = src ? std::shared_ptr<utils::shm>(*src) : std::make_shared<utils::shm>(size);
std::shared_ptr<utils::shm> shm;
if (m_common)
verify(HERE), !src;
else if (src)
shm = *src;
else
shm = std::make_shared<utils::shm>(size);
// Search for an appropriate place (unoptimized)
for (u32 addr = ::align(this->addr, align); addr < this->addr + this->size - 1; addr += align)
{
if (try_alloc(addr, pflags, std::move(shm)))
if (try_alloc(addr, pflags, size, std::move(shm)))
{
return addr;
}
@ -609,8 +647,11 @@ namespace vm
{
vm::writer_lock lock(0);
// align to minimal page size
const u32 size = ::align(orig_size, 0x10000);
// Determine minimal alignment
const u32 min_page_size = flags & 0x100 ? 0x1000 : 0x10000;
// Align to minimal page size
const u32 size = ::align(orig_size, min_page_size);
// return if addr or size is invalid
if (!size || size > this->size || addr < this->addr || addr + size - 1 > this->addr + this->size - 1)
@ -629,7 +670,17 @@ namespace vm
pflags |= page_64k_size;
}
if (!try_alloc(addr, pflags, src ? std::shared_ptr<utils::shm>(*src) : std::make_shared<utils::shm>(size)))
// Create or import shared memory object
std::shared_ptr<utils::shm> shm;
if (m_common)
verify(HERE), !src;
else if (src)
shm = *src;
else
shm = std::make_shared<utils::shm>(size);
if (!try_alloc(addr, pflags, size, std::move(shm)))
{
return 0;
}
@ -655,10 +706,12 @@ namespace vm
return 0;
}
result = found->second->size();
// Approximate allocation size
const auto next = std::next(found);
const auto size = (next == m_map.end() ? this->addr + this->size : next->first) - found->first;
// Unmap "real" memory pages
_page_unmap(addr, *found->second);
result = _page_unmap(addr, size, found->second.get());
// Remove entry
m_map.erase(found);
@ -697,6 +750,12 @@ namespace vm
return {addr, nullptr};
}
// Special path
if (m_common)
{
return {this->addr, m_common};
}
// Range check
if (std::max<u32>(size, addr - found->first + size) > found->second->size())
{
@ -896,7 +955,7 @@ namespace vm
std::make_shared<block_t>(0x20000000, 0x10000000, 0x201), // user 64k pages
nullptr, // user 1m pages
std::make_shared<block_t>(0xC0000000, 0x10000000), // video
std::make_shared<block_t>(0xD0000000, 0x10000000), // stack
std::make_shared<block_t>(0xD0000000, 0x10000000, 0x101), // stack
std::make_shared<block_t>(0xE0000000, 0x20000000), // SPU reserved
};
}

View File

@ -146,7 +146,10 @@ namespace vm
// Mapped regions: addr -> shm handle
std::map<u32, std::shared_ptr<utils::shm>> m_map;
bool try_alloc(u32 addr, u8 flags, std::shared_ptr<utils::shm>&&);
// Common mapped region for special cases
std::shared_ptr<utils::shm> m_common;
bool try_alloc(u32 addr, u8 flags, u32 size, std::shared_ptr<utils::shm>&&);
public:
block_t(u32 addr, u32 size, u64 flags = 0);