2015-05-24 04:55:12 +00:00
|
|
|
// Copyright 2008 Dolphin Emulator Project
|
2015-05-17 23:08:10 +00:00
|
|
|
// Licensed under GPLv2+
|
2013-04-18 03:09:55 +00:00
|
|
|
// Refer to the license.txt file included.
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-02-20 03:11:52 +00:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdlib>
|
2013-10-19 22:58:02 +00:00
|
|
|
#include <set>
|
2014-02-20 03:11:52 +00:00
|
|
|
#include <string>
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2017-04-14 10:53:32 +00:00
|
|
|
#include "Common/CommonFuncs.h"
|
2014-09-08 01:06:58 +00:00
|
|
|
#include "Common/CommonTypes.h"
|
2015-09-26 21:13:07 +00:00
|
|
|
#include "Common/Logging/Log.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Common/MemArena.h"
|
2015-09-26 21:13:07 +00:00
|
|
|
#include "Common/MsgHandler.h"
|
2014-02-17 10:18:15 +00:00
|
|
|
#include "Common/StringUtil.h"
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2009-01-16 02:58:34 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
#include <windows.h>
|
|
|
|
#else
|
2008-12-08 05:30:24 +00:00
|
|
|
#include <cerrno>
|
|
|
|
#include <cstring>
|
2014-02-20 03:11:52 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/mman.h>
|
|
|
|
#include <unistd.h>
|
2013-02-26 19:49:00 +00:00
|
|
|
#ifdef ANDROID
|
|
|
|
#include <linux/ashmem.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#endif
|
2008-12-08 05:30:24 +00:00
|
|
|
#endif
|
|
|
|
|
2018-05-15 21:28:41 +00:00
|
|
|
namespace Common
|
|
|
|
{
|
2013-02-26 19:49:00 +00:00
|
|
|
#ifdef ANDROID
|
|
|
|
#define ASHMEM_DEVICE "/dev/ashmem"
|
|
|
|
|
2016-01-21 19:46:25 +00:00
|
|
|
static int AshmemCreateFileMapping(const char* name, size_t size)
|
2013-02-26 19:49:00 +00:00
|
|
|
{
|
|
|
|
int fd, ret;
|
|
|
|
fd = open(ASHMEM_DEVICE, O_RDWR);
|
|
|
|
if (fd < 0)
|
|
|
|
return fd;
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-10-29 05:09:01 +00:00
|
|
|
// We don't really care if we can't set the name, it is optional
|
2014-02-23 22:03:39 +00:00
|
|
|
ioctl(fd, ASHMEM_SET_NAME, name);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2013-02-26 19:49:00 +00:00
|
|
|
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
|
|
|
|
if (ret < 0)
|
|
|
|
{
|
|
|
|
close(fd);
|
|
|
|
NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
#endif
|
2008-12-08 05:30:24 +00:00
|
|
|
|
2014-11-01 20:30:14 +00:00
|
|
|
void MemArena::GrabSHMSegment(size_t size)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
2017-10-11 15:17:25 +00:00
|
|
|
const std::string name = "dolphin-emu." + std::to_string(GetCurrentProcessId());
|
2017-09-30 18:59:07 +00:00
|
|
|
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
|
2017-10-11 15:17:25 +00:00
|
|
|
static_cast<DWORD>(size), UTF8ToTStr(name).c_str());
|
2013-02-26 19:49:00 +00:00
|
|
|
#elif defined(ANDROID)
|
2017-10-11 15:17:25 +00:00
|
|
|
fd = AshmemCreateFileMapping(("dolphin-emu." + std::to_string(getpid())).c_str(), size);
|
2013-02-26 19:49:00 +00:00
|
|
|
if (fd < 0)
|
|
|
|
{
|
|
|
|
NOTICE_LOG(MEMMAP, "Ashmem allocation failed");
|
|
|
|
return;
|
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
#else
|
2017-10-11 15:17:25 +00:00
|
|
|
const std::string file_name = "/dolphin-emu." + std::to_string(getpid());
|
|
|
|
fd = shm_open(file_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
|
|
|
|
if (fd == -1)
|
Fix an idiotic race condition when starting games in multiple Dolphin instances at the same time on Unix.
MemArena mmaps the emulated memory from a file in order to get the same
mapping at multiple addresses. A file which, formerly, was located at a
static filename: it was unlinked after creation, but the open did not
use O_EXCL, so if two instances started up on the same system at just
the right time, they would get the same memory. Naturally, this caused
extremely mysterious crashes, but only in Netplay, where the game is
automatically started when the client receives a broadcast from the
server, so races are actually quite likely.
And switch to shm_open, because it fits the bill better and avoids any
issues with using /tmp.
2013-12-10 05:27:20 +00:00
|
|
|
{
|
2017-10-11 15:17:25 +00:00
|
|
|
ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno));
|
|
|
|
return;
|
Fix an idiotic race condition when starting games in multiple Dolphin instances at the same time on Unix.
MemArena mmaps the emulated memory from a file in order to get the same
mapping at multiple addresses. A file which, formerly, was located at a
static filename: it was unlinked after creation, but the open did not
use O_EXCL, so if two instances started up on the same system at just
the right time, they would get the same memory. Naturally, this caused
extremely mysterious crashes, but only in Netplay, where the game is
automatically started when the client receives a broadcast from the
server, so races are actually quite likely.
And switch to shm_open, because it fits the bill better and avoids any
issues with using /tmp.
2013-12-10 05:27:20 +00:00
|
|
|
}
|
2017-10-11 15:17:25 +00:00
|
|
|
shm_unlink(file_name.c_str());
|
2013-01-31 21:29:29 +00:00
|
|
|
if (ftruncate(fd, size) < 0)
|
|
|
|
ERROR_LOG(MEMMAP, "Failed to allocate low memory space");
|
2008-12-08 05:30:24 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-11-01 20:30:14 +00:00
|
|
|
void MemArena::ReleaseSHMSegment()
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
CloseHandle(hMemoryMapping);
|
|
|
|
hMemoryMapping = 0;
|
|
|
|
#else
|
|
|
|
close(fd);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-01-21 20:16:51 +00:00
|
|
|
void* MemArena::CreateView(s64 offset, size_t size, void* base)
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
2009-01-19 21:42:24 +00:00
|
|
|
return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
|
2008-12-08 05:30:24 +00:00
|
|
|
#else
|
2012-07-12 03:54:50 +00:00
|
|
|
void* retval = mmap(base, size, PROT_READ | PROT_WRITE,
|
|
|
|
MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
|
2016-06-24 08:43:46 +00:00
|
|
|
|
2012-07-12 03:54:50 +00:00
|
|
|
if (retval == MAP_FAILED)
|
|
|
|
{
|
Fix an idiotic race condition when starting games in multiple Dolphin instances at the same time on Unix.
MemArena mmaps the emulated memory from a file in order to get the same
mapping at multiple addresses. A file which, formerly, was located at a
static filename: it was unlinked after creation, but the open did not
use O_EXCL, so if two instances started up on the same system at just
the right time, they would get the same memory. Naturally, this caused
extremely mysterious crashes, but only in Netplay, where the game is
automatically started when the client receives a broadcast from the
server, so races are actually quite likely.
And switch to shm_open, because it fits the bill better and avoids any
issues with using /tmp.
2013-12-10 05:27:20 +00:00
|
|
|
NOTICE_LOG(MEMMAP, "mmap failed");
|
2012-07-12 03:54:50 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return retval;
|
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void MemArena::ReleaseView(void* view, size_t size)
|
|
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
|
|
UnmapViewOfFile(view);
|
|
|
|
#else
|
|
|
|
munmap(view, size);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-01-17 21:17:36 +00:00
|
|
|
u8* MemArena::FindMemoryBase()
|
2008-12-08 05:30:24 +00:00
|
|
|
{
|
2017-04-14 10:53:32 +00:00
|
|
|
#if _ARCH_32
|
|
|
|
const size_t memory_size = 0x31000000;
|
|
|
|
#else
|
|
|
|
const size_t memory_size = 0x400000000;
|
|
|
|
#endif
|
|
|
|
|
2008-12-08 05:30:24 +00:00
|
|
|
#ifdef _WIN32
|
2017-04-14 10:53:32 +00:00
|
|
|
u8* base = static_cast<u8*>(VirtualAlloc(nullptr, memory_size, MEM_RESERVE, PAGE_READWRITE));
|
|
|
|
if (!base)
|
|
|
|
{
|
2017-08-17 19:12:44 +00:00
|
|
|
PanicAlert("Failed to map enough memory space: %s", GetLastErrorString().c_str());
|
2017-04-14 10:53:32 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
2008-12-08 05:30:24 +00:00
|
|
|
VirtualFree(base, 0, MEM_RELEASE);
|
|
|
|
return base;
|
|
|
|
#else
|
2013-03-25 02:06:34 +00:00
|
|
|
#ifdef ANDROID
|
2013-09-02 09:10:21 +00:00
|
|
|
// Android 4.3 changed how mmap works.
|
|
|
|
// if we map it private and then munmap it, we can't use the base returned.
|
|
|
|
// This may be due to changes in them support a full SELinux implementation.
|
2013-09-30 01:53:32 +00:00
|
|
|
const int flags = MAP_ANON | MAP_SHARED;
|
2013-03-25 02:06:34 +00:00
|
|
|
#else
|
2013-09-02 09:10:21 +00:00
|
|
|
const int flags = MAP_ANON | MAP_PRIVATE;
|
2013-03-25 02:06:34 +00:00
|
|
|
#endif
|
2017-04-14 10:53:32 +00:00
|
|
|
void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
|
2014-08-30 20:14:56 +00:00
|
|
|
if (base == MAP_FAILED)
|
|
|
|
{
|
2017-08-17 19:12:44 +00:00
|
|
|
PanicAlert("Failed to map enough memory space: %s", LastStrerrorString().c_str());
|
2017-04-14 10:53:32 +00:00
|
|
|
return nullptr;
|
2008-12-08 05:30:24 +00:00
|
|
|
}
|
2017-04-14 10:53:32 +00:00
|
|
|
munmap(base, memory_size);
|
2008-12-08 05:30:24 +00:00
|
|
|
return static_cast<u8*>(base);
|
|
|
|
#endif
|
|
|
|
}
|
2018-05-15 21:28:41 +00:00
|
|
|
|
|
|
|
} // namespace Common
|