Re-implemented vm from FS-UAE
This commit is contained in:
parent
e8eec8443c
commit
3ce62a705d
3 changed files with 511 additions and 0 deletions
1
Makefile
1
Makefile
|
@ -332,6 +332,7 @@ OBJS = \
|
|||
src/traps.o \
|
||||
src/uaelib.o \
|
||||
src/uaeresource.o \
|
||||
src/vm.o \
|
||||
src/zfile.o \
|
||||
src/zfile_archive.o \
|
||||
src/archivers/7z/7zAlloc.o \
|
||||
|
|
49
src/include/uae/vm.h
Normal file
49
src/include/uae/vm.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Multi-platform virtual memory functions for UAE.
|
||||
* Copyright (C) 2015 Frode Solheim
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License version 2.
|
||||
* See the file 'COPYING' for full license text.
|
||||
*/
|
||||
|
||||
#ifndef UAE_VM_H
|
||||
#define UAE_VM_H
|
||||
|
||||
#include "uae/types.h"
|
||||
|
||||
#define UAE_VM_WRITE 2
|
||||
#define UAE_VM_EXECUTE 4
|
||||
|
||||
#define UAE_VM_32BIT (1 << 8)
|
||||
#define UAE_VM_WRITE_WATCH (1 << 9)
|
||||
#define UAE_VM_ALLOC_FAILED NULL
|
||||
|
||||
/* Even though it looks like you can OR together vm protection values,
|
||||
* do not do this. Not all combinations are supported (on Windows), and only
|
||||
* a few combinations are implemented. Only use the following predefined
|
||||
* constants to be safe. */
|
||||
|
||||
#define UAE_VM_NO_ACCESS 0
|
||||
#define UAE_VM_READ 1
|
||||
#define UAE_VM_READ_WRITE (UAE_VM_READ | UAE_VM_WRITE)
|
||||
#define UAE_VM_READ_EXECUTE (UAE_VM_READ | UAE_VM_EXECUTE)
|
||||
#define UAE_VM_READ_WRITE_EXECUTE (UAE_VM_READ | UAE_VM_WRITE | UAE_VM_EXECUTE)
|
||||
|
||||
#if 0
|
||||
void *uae_vm_alloc(uae_u32 size);
|
||||
void *uae_vm_alloc(uae_u32 size, int flags);
|
||||
#endif
|
||||
void *uae_vm_alloc(uae_u32 size, int flags, int protect);
|
||||
bool uae_vm_protect(void *address, int size, int protect);
|
||||
bool uae_vm_free(void *address, int size);
|
||||
|
||||
void *uae_vm_reserve(uae_u32 size, int flags);
|
||||
void *uae_vm_reserve_fixed(void *address, uae_u32 size, int flags);
|
||||
void *uae_vm_commit(void *address, uae_u32 size, int protect);
|
||||
bool uae_vm_decommit(void *address, uae_u32 size);
|
||||
|
||||
int uae_vm_page_size(void);
|
||||
|
||||
// void *uae_vm_alloc_with_flags(uae_u32 size, int protect, int flags);
|
||||
|
||||
#endif /* UAE_VM_H */
|
461
src/vm.cpp
Normal file
461
src/vm.cpp
Normal file
|
@ -0,0 +1,461 @@
|
|||
/*
|
||||
* Multi-platform virtual memory functions for UAE.
|
||||
* Copyright (C) 2015 Frode Solheim
|
||||
*
|
||||
* Licensed under the terms of the GNU General Public License version 2.
|
||||
* See the file 'COPYING' for full license text.
|
||||
*/
|
||||
|
||||
#include "sysconfig.h"
|
||||
#include "sysdeps.h"
|
||||
#include "uae/vm.h"
|
||||
#include "options.h"
|
||||
#include "memory.h"
|
||||
#ifdef _WIN32
|
||||
|
||||
#else
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#include <sys/sysctl.h>
|
||||
#endif
|
||||
|
||||
#if defined(LINUX) && defined(CPU_x86_64)
|
||||
#define HAVE_MAP_32BIT 1
|
||||
#endif
|
||||
|
||||
// #define CLEAR_MEMORY_ON_COMMIT
|
||||
|
||||
// #define LOG_ALLOCATIONS
|
||||
// #define TRACK_ALLOCATIONS
|
||||
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
|
||||
struct alloc_size {
|
||||
void *address;
|
||||
uae_u32 size;
|
||||
};
|
||||
|
||||
#define MAX_ALLOCATIONS 2048
|
||||
/* A bit inefficient, but good enough for few and rare allocs. Storing
|
||||
* the size at the start of the allocated memory would be better, but this
|
||||
* could be awkward if/when you want to allocate page-aligned memory. */
|
||||
static struct alloc_size alloc_sizes[MAX_ALLOCATIONS];
|
||||
|
||||
static void add_allocation(void *address, uae_u32 size)
|
||||
{
|
||||
uae_log("VM: add_allocation %p (%d)\n", address, size);
|
||||
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
|
||||
if (alloc_sizes[i].address == NULL) {
|
||||
alloc_sizes[i].address = address;
|
||||
alloc_sizes[i].size = size;
|
||||
return;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static uae_u32 find_allocation(void *address)
|
||||
{
|
||||
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
|
||||
if (alloc_sizes[i].address == address) {
|
||||
return alloc_sizes[i].size;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
static uae_u32 remove_allocation(void *address)
|
||||
{
|
||||
for (int i = 0; i < MAX_ALLOCATIONS; i++) {
|
||||
if (alloc_sizes[i].address == address) {
|
||||
alloc_sizes[i].address = NULL;
|
||||
uae_u32 size = alloc_sizes[i].size;
|
||||
alloc_sizes[i].size = 0;
|
||||
return size;
|
||||
}
|
||||
}
|
||||
abort();
|
||||
}
|
||||
|
||||
#endif /* TRACK_ALLOCATIONS */
|
||||
|
||||
static int protect_to_native(int protect)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (protect == UAE_VM_NO_ACCESS) return PAGE_NOACCESS;
|
||||
if (protect == UAE_VM_READ) return PAGE_READONLY;
|
||||
if (protect == UAE_VM_READ_WRITE) return PAGE_READWRITE;
|
||||
if (protect == UAE_VM_READ_EXECUTE) return PAGE_EXECUTE_READ;
|
||||
if (protect == UAE_VM_READ_WRITE_EXECUTE) return PAGE_EXECUTE_READWRITE;
|
||||
uae_log("VM: Invalid protect value %d\n", protect);
|
||||
return PAGE_NOACCESS;
|
||||
#else
|
||||
if (protect == UAE_VM_NO_ACCESS) return PROT_NONE;
|
||||
if (protect == UAE_VM_READ) return PROT_READ;
|
||||
if (protect == UAE_VM_READ_WRITE) return PROT_READ | PROT_WRITE;
|
||||
if (protect == UAE_VM_READ_EXECUTE) return PROT_READ | PROT_EXEC;
|
||||
if (protect == UAE_VM_READ_WRITE_EXECUTE) {
|
||||
return PROT_READ | PROT_WRITE | PROT_EXEC;
|
||||
}
|
||||
write_log("VM: Invalid protect value %d\n", protect);
|
||||
return PROT_NONE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static const char *protect_description(int protect)
|
||||
{
|
||||
if (protect == UAE_VM_NO_ACCESS) return "NO_ACCESS";
|
||||
if (protect == UAE_VM_READ) return "READ";
|
||||
if (protect == UAE_VM_READ_WRITE) return "READ_WRITE";
|
||||
if (protect == UAE_VM_READ_EXECUTE) return "READ_EXECUTE";
|
||||
if (protect == UAE_VM_READ_WRITE_EXECUTE) return "READ_WRITE_EXECUTE";
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
int uae_vm_page_size(void)
|
||||
{
|
||||
static int page_size = 0;
|
||||
if (page_size == 0) {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
page_size = si.dwPageSize;
|
||||
#else
|
||||
page_size = sysconf(_SC_PAGESIZE);
|
||||
#endif
|
||||
}
|
||||
return page_size;
|
||||
}
|
||||
|
||||
static void *try_alloc_32bit(uae_u32 size, int native_flags, int native_protect,
|
||||
uae_u8 *p, uae_u8 *p_end)
|
||||
{
|
||||
if (p_end <= p) {
|
||||
return NULL;
|
||||
}
|
||||
if ((uintptr_t) p % uae_vm_page_size() != 0) {
|
||||
/* Round up to the nearest page size */
|
||||
p += uae_vm_page_size() - (uintptr_t) p % uae_vm_page_size();
|
||||
}
|
||||
void *address = NULL;
|
||||
int step = uae_vm_page_size();
|
||||
if (size > 1024 * 1024) {
|
||||
/* Reserve some space for smaller allocations */
|
||||
p += 32 * 1024 * 1024;
|
||||
step = 1024 * 1024;
|
||||
}
|
||||
#ifdef HAVE_MAP_32BIT
|
||||
address = mmap(0, size, native_protect, native_flags | MAP_32BIT, -1, 0);
|
||||
if (address == MAP_FAILED) {
|
||||
address = NULL;
|
||||
}
|
||||
#endif
|
||||
while (address == NULL) {
|
||||
if (p > p_end) {
|
||||
break;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
address = VirtualAlloc(p, size, native_flags, native_protect);
|
||||
#else
|
||||
address = mmap(p, size, native_protect, native_flags, -1, 0);
|
||||
#ifdef LOG_ALLOCATIONS
|
||||
write_log("VM: trying %p step is 0x%x = %p\n", p, step, address);
|
||||
#endif
|
||||
if (address == MAP_FAILED) {
|
||||
address = NULL;
|
||||
} else if (((uintptr_t) address) + size > (uintptr_t) 0xffffffff) {
|
||||
munmap(address, size);
|
||||
address = NULL;
|
||||
}
|
||||
#endif
|
||||
p += step;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
static void *uae_vm_alloc_with_flags(uae_u32 size, int flags, int protect)
|
||||
{
|
||||
void *address = NULL;
|
||||
static bool first_allocation = true;
|
||||
if (first_allocation) {
|
||||
/* FIXME: log contents of /proc/self/maps on Linux */
|
||||
/* FIXME: use VirtualQuery function on Windows? */
|
||||
first_allocation = false;
|
||||
}
|
||||
#ifdef LOG_ALLOCATIONS
|
||||
uae_log("VM: Allocate 0x%-8x bytes [%d] (%s)\n",
|
||||
size, flags, protect_description(protect));
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
int native_flags = MEM_COMMIT | MEM_RESERVE;
|
||||
if (flags & UAE_VM_WRITE_WATCH) {
|
||||
native_flags |= MEM_WRITE_WATCH;
|
||||
}
|
||||
int native_protect = protect_to_native(protect);
|
||||
#else
|
||||
int native_flags = MAP_PRIVATE | MAP_ANON;
|
||||
int native_protect = protect_to_native(protect);
|
||||
#endif
|
||||
|
||||
#ifndef CPU_64_BIT
|
||||
flags &= ~UAE_VM_32BIT;
|
||||
#endif
|
||||
if (flags & UAE_VM_32BIT) {
|
||||
/* Stupid algorithm to find available space, but should
|
||||
* work well enough when there is not a lot of allocations. */
|
||||
/* FIXME: Consider allocating a bigger chunk of memory, and manually
|
||||
* keep track of allocations. */
|
||||
#if 1
|
||||
if (!address) {
|
||||
address = try_alloc_32bit(
|
||||
size, native_flags, native_protect,
|
||||
(uae_u8 *) 0x40000000, natmem_reserved - size);
|
||||
}
|
||||
#endif
|
||||
if (!address && natmem_reserved < (uae_u8 *) 0x60000000) {
|
||||
address = try_alloc_32bit(
|
||||
size, native_flags, native_protect,
|
||||
(uae_u8 *) natmem_reserved + natmem_reserved_size,
|
||||
(uae_u8 *) 0xffffffff - size + 1);
|
||||
}
|
||||
if (!address) {
|
||||
address = try_alloc_32bit(
|
||||
size, native_flags, native_protect,
|
||||
(uae_u8 *) 0x20000000,
|
||||
min((uae_u8 *) 0x40000000, natmem_reserved - size));
|
||||
}
|
||||
} else {
|
||||
#ifdef _WIN32
|
||||
address = VirtualAlloc(NULL, size, native_flags, native_protect);
|
||||
#else
|
||||
address = mmap(0, size, native_protect, native_flags, -1, 0);
|
||||
if (address == MAP_FAILED) {
|
||||
address = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if (address == NULL) {
|
||||
write_log("VM: uae_vm_alloc(%u, %d, %d) mmap failed (%d)\n",
|
||||
size, flags, protect, errno);
|
||||
return NULL;
|
||||
}
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
add_allocation(address, size);
|
||||
#endif
|
||||
#ifdef LOG_ALLOCATIONS
|
||||
uae_log("VM: %p\n", address);
|
||||
#endif
|
||||
return address;
|
||||
}
|
||||
|
||||
void *uae_vm_alloc(uae_u32 size, int flags, int protect)
|
||||
{
|
||||
return uae_vm_alloc_with_flags(size, flags, protect);
|
||||
}
|
||||
|
||||
static bool do_protect(void *address, int size, int protect)
|
||||
{
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
uae_u32 allocated_size = find_allocation(address);
|
||||
assert(allocated_size == size);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
DWORD old;
|
||||
if (VirtualProtect(address, size, protect_to_native(protect), &old) == 0) {
|
||||
uae_log("VM: uae_vm_protect(%p, %d, %d) VirtualProtect failed (%d)\n",
|
||||
address, size, protect, GetLastError());
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
if (mprotect(address, size, protect_to_native(protect)) != 0) {
|
||||
write_log("VM: uae_vm_protect(%p, %d, %d) mprotect failed (%d)\n",
|
||||
address, size, protect, errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uae_vm_protect(void *address, int size, int protect)
|
||||
{
|
||||
return do_protect(address, size, protect);
|
||||
}
|
||||
|
||||
static bool do_free(void *address, int size)
|
||||
{
|
||||
#ifdef TRACK_ALLOCATIONS
|
||||
uae_u32 allocated_size = remove_allocation(address);
|
||||
assert(allocated_size == size);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
return VirtualFree(address, 0, MEM_RELEASE) != 0;
|
||||
#else
|
||||
if (munmap(address, size) != 0) {
|
||||
write_log("VM: uae_vm_free(%p, %d) munmap failed (%d)\n",
|
||||
address, size, errno);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uae_vm_free(void *address, int size)
|
||||
{
|
||||
write_log("VM: Free 0x%-8x bytes at %p\n", size, address);
|
||||
return do_free(address, size);
|
||||
}
|
||||
|
||||
static void *try_reserve(uintptr_t try_addr, uae_u32 size, int flags)
|
||||
{
|
||||
void *address = NULL;
|
||||
if (try_addr) {
|
||||
write_log("VM: Reserve 0x%-8x bytes, try address 0x%llx\n",
|
||||
size, (uae_u64) try_addr);
|
||||
} else {
|
||||
write_log("VM: Reserve 0x%-8x bytes\n", size);
|
||||
}
|
||||
#ifdef _WIN32
|
||||
int va_type = MEM_RESERVE;
|
||||
if (flags & UAE_VM_WRITE_WATCH) {
|
||||
va_type |= MEM_WRITE_WATCH;
|
||||
}
|
||||
int va_protect = protect_to_native(UAE_VM_NO_ACCESS);
|
||||
address = VirtualAlloc((void *) try_addr, size, va_type, va_protect);
|
||||
if (address == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
int mmap_flags = MAP_PRIVATE | MAP_ANON;
|
||||
address = mmap((void *) try_addr, size, PROT_NONE, mmap_flags, -1, 0);
|
||||
if (address == MAP_FAILED) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
#ifdef CPU_64_BIT
|
||||
if (flags & UAE_VM_32BIT) {
|
||||
uintptr_t end = (uintptr_t) address + size;
|
||||
if (address && end > (uintptr_t) 0x100000000ULL) {
|
||||
uae_log("VM: Reserve 0x%-8x bytes, got address 0x%llx (> 32-bit)\n",
|
||||
size, (uae_u64) (uintptr_t) address);
|
||||
#ifdef _WIN32
|
||||
VirtualFree(address, 0, MEM_RELEASE);
|
||||
#else
|
||||
munmap(address, size);
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return address;
|
||||
}
|
||||
|
||||
void *uae_vm_reserve(uae_u32 size, int flags)
|
||||
{
|
||||
#if 0
|
||||
if (size > 768 * 1024 * 1024) {
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
void *address = NULL;
|
||||
#ifdef _WIN32
|
||||
address = try_reserve(0x80000000, size, flags);
|
||||
if (address == NULL && (flags & UAE_VM_32BIT)) {
|
||||
if (size <= 768 * 1024 * 1024) {
|
||||
address = try_reserve(0x78000000 - size, size, flags);
|
||||
}
|
||||
}
|
||||
if (address == NULL && (flags & UAE_VM_32BIT) == 0) {
|
||||
address = try_reserve(0, size, flags);
|
||||
}
|
||||
#else
|
||||
#ifdef CPU_64_BIT
|
||||
if (flags & UAE_VM_32BIT) {
|
||||
#else
|
||||
if (true) {
|
||||
#endif
|
||||
uintptr_t try_addr = 0xffffffff - size + 1;
|
||||
while (address == NULL) {
|
||||
address = try_reserve(try_addr, size, flags);
|
||||
if (address == NULL) {
|
||||
try_addr -= 0x4000000;
|
||||
if (try_addr < 0x20000000) {
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (address == NULL && (flags & UAE_VM_32BIT) == 0) {
|
||||
address = try_reserve(0, size, flags);
|
||||
}
|
||||
#endif
|
||||
if (address) {
|
||||
write_log("VM: Reserve 0x%-8x bytes, got address 0x%llx\n",
|
||||
size, (uae_u64) (uintptr_t) address);
|
||||
} else {
|
||||
write_log("VM: Reserve 0x%-8x bytes failed!\n", size);
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
void *uae_vm_reserve_fixed(void *want_addr, uae_u32 size, int flags)
|
||||
{
|
||||
void *address = NULL;
|
||||
write_log("VM: Reserve 0x%-8x bytes at %p (fixed)\n", size, want_addr);
|
||||
address = try_reserve((uintptr_t) want_addr, size, flags);
|
||||
if (address == NULL) {
|
||||
write_log("VM: Reserve 0x%-8x bytes at %p failed!\n", size, want_addr);
|
||||
return NULL;
|
||||
}
|
||||
if (address != want_addr) {
|
||||
do_free(address, size);
|
||||
return NULL;
|
||||
}
|
||||
write_log("VM: Reserve 0x%-8x bytes, got address 0x%llx\n",
|
||||
size, (uae_u64) (uintptr_t) address);
|
||||
return address;
|
||||
}
|
||||
|
||||
void *uae_vm_commit(void *address, uae_u32 size, int protect)
|
||||
{
|
||||
write_log("VM: Commit 0x%-8x bytes at %p (%s)\n",
|
||||
size, address, protect_description(protect));
|
||||
#ifdef _WIN32
|
||||
int va_type = MEM_COMMIT ;
|
||||
int va_protect = protect_to_native(protect);
|
||||
address = VirtualAlloc(address, size, va_type, va_protect);
|
||||
#else
|
||||
#ifdef CLEAR_MEMORY_ON_COMMIT
|
||||
do_protect(address, size, UAE_VM_READ_WRITE);
|
||||
memset(address, 0, size);
|
||||
#endif
|
||||
do_protect(address, size, protect);
|
||||
#endif
|
||||
return address;
|
||||
}
|
||||
|
||||
bool uae_vm_decommit(void *address, uae_u32 size)
|
||||
{
|
||||
write_log("VM: Decommit 0x%-8x bytes at %p\n", size, address);
|
||||
#ifdef _WIN32
|
||||
return VirtualFree (address, size, MEM_DECOMMIT) != 0;
|
||||
#else
|
||||
/* Re-map the memory so we get fresh unused pages (and the old ones can be
|
||||
* released and physical memory reclaimed). We also assume that the new
|
||||
* pages will be zero-initialized (tested on Linux and OS X). */
|
||||
void *result = mmap(address, size, PROT_NONE,
|
||||
MAP_ANON | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
if (result == MAP_FAILED) {
|
||||
write_log("VM: Warning - could not re-map with MAP_FIXED at %p\n",
|
||||
address);
|
||||
do_protect(address, size, UAE_VM_NO_ACCESS);
|
||||
}
|
||||
return result != MAP_FAILED;
|
||||
#endif
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue