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