1 // Copyright 2008 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include <cstddef>
6 #include <cstdlib>
7 #include <set>
8 #include <string>
9 
10 #include "Common/CommonFuncs.h"
11 #include "Common/CommonTypes.h"
12 #include "Common/Logging/Log.h"
13 #include "Common/MemArena.h"
14 #include "Common/MsgHandler.h"
15 #include "Common/StringUtil.h"
16 
17 #ifdef _WIN32
18 #include <windows.h>
19 #else
20 #include <cerrno>
21 #include <cstring>
22 #include <fcntl.h>
23 #include <sys/mman.h>
24 #include <unistd.h>
25 #ifdef ANDROID
26 #include <dlfcn.h>
27 #include <linux/ashmem.h>
28 #include <sys/ioctl.h>
29 #endif
30 #endif
31 
32 namespace Common
33 {
34 #ifdef ANDROID
35 #define ASHMEM_DEVICE "/dev/ashmem"
36 
AshmemCreateFileMapping(const char * name,size_t size)37 static int AshmemCreateFileMapping(const char* name, size_t size)
38 {
39   // ASharedMemory path - works on API >= 26 and falls through on API < 26:
40 
41   // We can't call ASharedMemory_create the normal way without increasing the
42   // minimum version requirement to API 26, so we use dlopen/dlsym instead
43   static void* libandroid = dlopen("libandroid.so", RTLD_LAZY | RTLD_LOCAL);
44   static auto shared_memory_create =
45       reinterpret_cast<int (*)(const char*, size_t)>(dlsym(libandroid, "ASharedMemory_create"));
46   if (shared_memory_create)
47     return shared_memory_create(name, size);
48 
49   // /dev/ashmem path - works on API < 29:
50 
51   int fd, ret;
52   fd = open(ASHMEM_DEVICE, O_RDWR);
53   if (fd < 0)
54     return fd;
55 
56   // We don't really care if we can't set the name, it is optional
57   ioctl(fd, ASHMEM_SET_NAME, name);
58 
59   ret = ioctl(fd, ASHMEM_SET_SIZE, size);
60   if (ret < 0)
61   {
62     close(fd);
63     NOTICE_LOG(MEMMAP, "Ashmem returned error: 0x%08x", ret);
64     return ret;
65   }
66   return fd;
67 }
68 #endif
69 
GrabSHMSegment(size_t size)70 void MemArena::GrabSHMSegment(size_t size)
71 {
72 #ifdef _WIN32
73   const std::string name = "dolphin-emu." + std::to_string(GetCurrentProcessId());
74   hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0,
75                                      static_cast<DWORD>(size), UTF8ToTStr(name).c_str());
76 #elif defined(ANDROID)
77   fd = AshmemCreateFileMapping(("dolphin-emu." + std::to_string(getpid())).c_str(), size);
78   if (fd < 0)
79   {
80     NOTICE_LOG(MEMMAP, "Ashmem allocation failed");
81     return;
82   }
83 #else
84   const std::string file_name = "/dolphin-emu." + std::to_string(getpid());
85   fd = shm_open(file_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
86   if (fd == -1)
87   {
88     ERROR_LOG(MEMMAP, "shm_open failed: %s", strerror(errno));
89     return;
90   }
91   shm_unlink(file_name.c_str());
92   if (ftruncate(fd, size) < 0)
93     ERROR_LOG(MEMMAP, "Failed to allocate low memory space");
94 #endif
95 }
96 
ReleaseSHMSegment()97 void MemArena::ReleaseSHMSegment()
98 {
99 #ifdef _WIN32
100   CloseHandle(hMemoryMapping);
101   hMemoryMapping = 0;
102 #else
103   close(fd);
104 #endif
105 }
106 
CreateView(s64 offset,size_t size,void * base)107 void* MemArena::CreateView(s64 offset, size_t size, void* base)
108 {
109 #ifdef _WIN32
110   return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
111 #else
112   void* retval = mmap(base, size, PROT_READ | PROT_WRITE,
113                       MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
114 
115   if (retval == MAP_FAILED)
116   {
117     NOTICE_LOG(MEMMAP, "mmap failed");
118     return nullptr;
119   }
120   else
121   {
122     return retval;
123   }
124 #endif
125 }
126 
ReleaseView(void * view,size_t size)127 void MemArena::ReleaseView(void* view, size_t size)
128 {
129 #ifdef _WIN32
130   UnmapViewOfFile(view);
131 #else
132   munmap(view, size);
133 #endif
134 }
135 
FindMemoryBase()136 u8* MemArena::FindMemoryBase()
137 {
138 #if _ARCH_32
139   const size_t memory_size = 0x31000000;
140 #else
141   const size_t memory_size = 0x400000000;
142 #endif
143 
144 #ifdef _WIN32
145   u8* base = static_cast<u8*>(VirtualAlloc(nullptr, memory_size, MEM_RESERVE, PAGE_READWRITE));
146   if (!base)
147   {
148     PanicAlert("Failed to map enough memory space: %s", GetLastErrorString().c_str());
149     return nullptr;
150   }
151   VirtualFree(base, 0, MEM_RELEASE);
152   return base;
153 #else
154 #ifdef ANDROID
155   // Android 4.3 changed how mmap works.
156   // if we map it private and then munmap it, we can't use the base returned.
157   // This may be due to changes in them support a full SELinux implementation.
158   const int flags = MAP_ANON | MAP_SHARED;
159 #else
160   const int flags = MAP_ANON | MAP_PRIVATE;
161 #endif
162   void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
163   if (base == MAP_FAILED)
164   {
165     PanicAlert("Failed to map enough memory space: %s", LastStrerrorString().c_str());
166     return nullptr;
167   }
168   munmap(base, memory_size);
169   return static_cast<u8*>(base);
170 #endif
171 }
172 
173 }  // namespace Common
174