/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #include "xenia/base/memory.h" #include #include #include #include "xenia/base/math.h" #include "xenia/base/platform.h" #include "xenia/base/string.h" #if XE_PLATFORM_ANDROID #include #include #include #include "xenia/base/platform_android.h" #endif namespace xe { namespace memory { size_t page_size() { return getpagesize(); } size_t allocation_granularity() { return page_size(); } uint32_t ToPosixProtectFlags(PageAccess access) { switch (access) { case PageAccess::kNoAccess: return PROT_NONE; case PageAccess::kReadOnly: return PROT_READ; case PageAccess::kReadWrite: return PROT_READ | PROT_WRITE; case PageAccess::kExecuteReadOnly: return PROT_READ | PROT_EXEC; case PageAccess::kExecuteReadWrite: return PROT_READ | PROT_WRITE | PROT_EXEC; default: assert_unhandled_case(access); return PROT_NONE; } } bool IsWritableExecutableMemorySupported() { return true; } void* AllocFixed(void* base_address, size_t length, AllocationType allocation_type, PageAccess access) { // mmap does not support reserve / commit, so ignore allocation_type. uint32_t prot = ToPosixProtectFlags(access); void* result = mmap(base_address, length, prot, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0); if (result == MAP_FAILED) { return nullptr; } else { return result; } } bool DeallocFixed(void* base_address, size_t length, DeallocationType deallocation_type) { return munmap(base_address, length) == 0; } bool Protect(void* base_address, size_t length, PageAccess access, PageAccess* out_old_access) { // Linux does not have a syscall to query memory permissions. assert_null(out_old_access); uint32_t prot = ToPosixProtectFlags(access); return mprotect(base_address, length, prot) == 0; } bool QueryProtect(void* base_address, size_t& length, PageAccess& access_out) { return false; } FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path, size_t length, PageAccess access, bool commit) { #if XE_PLATFORM_ANDROID if (xe::platform::android::api_level() >= 26) { // TODO(Triang3l): Check if memfd can be used instead on API 30+. int sharedmem_fd = xe::platform::android::api_functions().api_26.ASharedMemory_create( path.c_str(), length); return sharedmem_fd >= 0 ? sharedmem_fd : kFileMappingHandleInvalid; } // Use /dev/ashmem on API versions below 26, which added ASharedMemory. // /dev/ashmem was disabled on API 29 for apps targeting it. // https://chromium.googlesource.com/chromium/src/+/master/third_party/ashmem/ashmem-dev.c int ashmem_fd = open("/" ASHMEM_NAME_DEF, O_RDWR); if (ashmem_fd < 0) { return kFileMappingHandleInvalid; } char ashmem_name[ASHMEM_NAME_LEN]; strlcpy(ashmem_name, path.c_str(), xe::countof(ashmem_name)); if (ioctl(ashmem_fd, ASHMEM_SET_NAME, ashmem_name) < 0 || ioctl(ashmem_fd, ASHMEM_SET_SIZE, length) < 0) { close(ashmem_fd); return kFileMappingHandleInvalid; } return ashmem_fd; #else int oflag; switch (access) { case PageAccess::kNoAccess: oflag = 0; break; case PageAccess::kReadOnly: case PageAccess::kExecuteReadOnly: oflag = O_RDONLY; break; case PageAccess::kReadWrite: case PageAccess::kExecuteReadWrite: oflag = O_RDWR; break; default: assert_always(); return kFileMappingHandleInvalid; } oflag |= O_CREAT; auto full_path = "/" / path; int ret = shm_open(full_path.c_str(), oflag, 0777); if (ret < 0) { return kFileMappingHandleInvalid; } ftruncate64(ret, length); return ret; #endif } void CloseFileMappingHandle(FileMappingHandle handle, const std::filesystem::path& path) { close(handle); #if !XE_PLATFORM_ANDROID auto full_path = "/" / path; shm_unlink(full_path.c_str()); #endif } void* MapFileView(FileMappingHandle handle, void* base_address, size_t length, PageAccess access, size_t file_offset) { uint32_t prot = ToPosixProtectFlags(access); return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle, file_offset); } bool UnmapFileView(FileMappingHandle handle, void* base_address, size_t length) { return munmap(base_address, length) == 0; } } // namespace memory } // namespace xe