mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
For some variants, /tmp will be tmpfs, but not always. In case our mmap fails to prevent flushing to disk (as apparently happens on Raspberry Pi), shm will work better for us. Thanks go to klyoungblood.
272 lines
6.8 KiB
C++
272 lines
6.8 KiB
C++
// Copyright (C) 2003 Dolphin Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
#include <string>
|
|
|
|
#include "FileUtil.h"
|
|
#include "MemoryUtil.h"
|
|
#include "MemArena.h"
|
|
|
|
#ifdef _WIN32
|
|
#include "CommonWindows.h"
|
|
#else
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <cerrno>
|
|
#include <cstring>
|
|
#ifdef ANDROID
|
|
#include <sys/ioctl.h>
|
|
#include <linux/ashmem.h>
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef ANDROID
|
|
|
|
// Hopefully this ABI will never change...
|
|
|
|
#define ASHMEM_DEVICE "/dev/ashmem"
|
|
|
|
/*
|
|
* ashmem_create_region - creates a new ashmem region and returns the file
|
|
* descriptor, or <0 on error
|
|
*
|
|
* `name' is an optional label to give the region (visible in /proc/pid/maps)
|
|
* `size' is the size of the region, in page-aligned bytes
|
|
*/
|
|
int ashmem_create_region(const char *name, size_t size) {
|
|
int fd, ret;
|
|
|
|
fd = open(ASHMEM_DEVICE, O_RDWR);
|
|
if (fd < 0)
|
|
return fd;
|
|
|
|
if (name) {
|
|
char buf[ASHMEM_NAME_LEN];
|
|
strncpy(buf, name, sizeof(buf));
|
|
ret = ioctl(fd, ASHMEM_SET_NAME, buf);
|
|
if (ret < 0)
|
|
goto error;
|
|
}
|
|
|
|
ret = ioctl(fd, ASHMEM_SET_SIZE, size);
|
|
if (ret < 0)
|
|
goto error;
|
|
|
|
return fd;
|
|
|
|
error:
|
|
ERROR_LOG(MEMMAP, "NASTY ASHMEM ERROR: ret = %08x", ret);
|
|
close(fd);
|
|
return ret;
|
|
}
|
|
|
|
int ashmem_set_prot_region(int fd, int prot) {
|
|
return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
|
|
}
|
|
|
|
int ashmem_pin_region(int fd, size_t offset, size_t len) {
|
|
// Even on 64-bit, it seems these arguments are 32-bit and thus need a cast to avoid warnings.
|
|
struct ashmem_pin pin = { (uint32_t)offset, (uint32_t)len };
|
|
return ioctl(fd, ASHMEM_PIN, &pin);
|
|
}
|
|
|
|
int ashmem_unpin_region(int fd, size_t offset, size_t len) {
|
|
struct ashmem_pin pin = { (uint32_t)offset, (uint32_t)len };
|
|
return ioctl(fd, ASHMEM_UNPIN, &pin);
|
|
}
|
|
#endif // Android
|
|
|
|
#ifndef _WIN32
|
|
static const std::string tmpfs_location = "/dev/shm";
|
|
static const std::string tmpfs_ram_temp_file = "/dev/shm/gc_mem.tmp";
|
|
|
|
// do not make this "static"
|
|
#ifdef MAEMO
|
|
std::string ram_temp_file = "/home/user/gc_mem.tmp";
|
|
#elif defined(BB)
|
|
std::string ram_temp_file = "/home/root/gc_mem.tmp";
|
|
#else
|
|
std::string ram_temp_file = "/tmp/gc_mem.tmp";
|
|
#endif
|
|
#elif !defined(_XBOX)
|
|
SYSTEM_INFO sysInfo;
|
|
#endif
|
|
|
|
|
|
// Windows mappings need to be on 64K boundaries, due to Alpha legacy.
|
|
#ifdef _WIN32
|
|
size_t MemArena::roundup(size_t x) {
|
|
#ifndef _XBOX
|
|
int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000;
|
|
#else
|
|
int gran = 0x10000; // 64k in 360
|
|
#endif
|
|
return (x + gran - 1) & ~(gran - 1);
|
|
}
|
|
#else
|
|
size_t MemArena::roundup(size_t x) {
|
|
return x;
|
|
}
|
|
#endif
|
|
|
|
void MemArena::GrabLowMemSpace(size_t size)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifndef _XBOX
|
|
hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, (DWORD)(size), NULL);
|
|
GetSystemInfo(&sysInfo);
|
|
#endif
|
|
#elif defined(ANDROID)
|
|
// Use ashmem so we don't have to allocate a file on disk!
|
|
fd = ashmem_create_region("PPSSPP_RAM", size);
|
|
// Note that it appears that ashmem is pinned by default, so no need to pin.
|
|
if (fd < 0) {
|
|
ERROR_LOG(MEMMAP, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
|
|
return;
|
|
}
|
|
#else
|
|
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
|
|
|
// Some platforms (like Raspberry Pi) end up flushing to disk.
|
|
// To avoid this, we try to use /dev/shm (tmpfs) if it exists.
|
|
fd = -1;
|
|
if (File::Exists(tmpfs_location)) {
|
|
fd = open(tmpfs_ram_temp_file.c_str(), O_RDWR | O_CREAT, mode);
|
|
if (fd >= 0) {
|
|
// Great, this definitely shouldn't flush to disk.
|
|
ram_temp_file = tmpfs_ram_temp_file;
|
|
}
|
|
}
|
|
|
|
if (fd < 0) {
|
|
fd = open(ram_temp_file.c_str(), O_RDWR | O_CREAT, mode);
|
|
}
|
|
if (fd < 0) {
|
|
ERROR_LOG(MEMMAP, "Failed to grab memory space as a file: %s of size: %08x errno: %d", ram_temp_file.c_str(), (int)size, (int)(errno));
|
|
return;
|
|
}
|
|
// delete immediately, we keep the fd so it still lives
|
|
if (unlink(ram_temp_file.c_str()) != 0) {
|
|
WARN_LOG(MEMMAP, "Failed to unlink %s", ram_temp_file.c_str());
|
|
}
|
|
if (ftruncate(fd, size) != 0) {
|
|
ERROR_LOG(MEMMAP, "Failed to ftruncate %d (%s) to size %08x", (int)fd, ram_temp_file.c_str(), (int)size);
|
|
}
|
|
return;
|
|
#endif
|
|
}
|
|
|
|
|
|
void MemArena::ReleaseSpace()
|
|
{
|
|
#ifdef _WIN32
|
|
CloseHandle(hMemoryMapping);
|
|
hMemoryMapping = 0;
|
|
#else
|
|
close(fd);
|
|
#endif
|
|
}
|
|
|
|
|
|
void *MemArena::CreateView(s64 offset, size_t size, void *base)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifdef _XBOX
|
|
size = roundup(size);
|
|
// use 64kb pages
|
|
void * ptr = VirtualAlloc(NULL, size, MEM_COMMIT|MEM_LARGE_PAGES, PAGE_READWRITE);
|
|
return ptr;
|
|
#else
|
|
size = roundup(size);
|
|
void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
|
|
return ptr;
|
|
#endif
|
|
#else
|
|
void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED |
|
|
// Do not sync memory to underlying file. Linux has this by default.
|
|
#ifdef BLACKBERRY
|
|
MAP_NOSYNCFILE |
|
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
|
MAP_NOSYNC |
|
|
#endif
|
|
((base == 0) ? 0 : MAP_FIXED), fd, offset);
|
|
|
|
if (retval == MAP_FAILED)
|
|
{
|
|
NOTICE_LOG(MEMMAP, "mmap on %s (fd: %d) failed", ram_temp_file.c_str(), (int)fd);
|
|
return 0;
|
|
}
|
|
return retval;
|
|
#endif
|
|
}
|
|
|
|
|
|
void MemArena::ReleaseView(void* view, size_t size)
|
|
{
|
|
#ifdef _WIN32
|
|
#ifndef _XBOX
|
|
UnmapViewOfFile(view);
|
|
#endif
|
|
#else
|
|
munmap(view, size);
|
|
#endif
|
|
}
|
|
|
|
u8* MemArena::Find4GBBase()
|
|
{
|
|
#ifdef _M_X64
|
|
#ifdef _WIN32
|
|
// 64 bit
|
|
u8 *base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE);
|
|
if (base) {
|
|
VirtualFree(base, 0, MEM_RELEASE);
|
|
}
|
|
return base;
|
|
#else
|
|
// Very precarious - mmap cannot return an error when trying to map already used pages.
|
|
// This makes the Windows approach above unusable on Linux, so we will simply pray...
|
|
return reinterpret_cast<u8*>(0x2300000000ULL);
|
|
#endif
|
|
|
|
#elif defined(ARM64)
|
|
|
|
// Very precarious - mmap cannot return an error when trying to map already used pages.
|
|
// This makes the Windows approach above unusable on Linux, so we will simply pray...
|
|
return reinterpret_cast<u8*>(0x2300000000ULL);
|
|
|
|
#else
|
|
|
|
#ifdef _WIN32
|
|
u8* base = (u8*)VirtualAlloc(0, 0x10000000, MEM_RESERVE, PAGE_READWRITE);
|
|
if (base) {
|
|
VirtualFree(base, 0, MEM_RELEASE);
|
|
}
|
|
return base;
|
|
#else
|
|
void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE,
|
|
MAP_ANON | MAP_SHARED, -1, 0);
|
|
if (base == MAP_FAILED) {
|
|
PanicAlert("Failed to map 256 MB of memory space: %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
munmap(base, 0x10000000);
|
|
return static_cast<u8*>(base);
|
|
#endif
|
|
#endif
|
|
}
|
|
|