1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "SharedMemoryAndroid"
18 
19 #include <android-base/logging.h>
20 #include <android-base/mapped_file.h>
21 #include <android-base/scopeguard.h>
22 #include <android/hardware_buffer.h>
23 
24 #include <algorithm>
25 #include <any>
26 #include <iterator>
27 #include <limits>
28 #include <memory>
29 #include <string>
30 #include <utility>
31 #include <variant>
32 #include <vector>
33 
34 #include "Result.h"
35 #include "SharedMemory.h"
36 #include "TypeUtils.h"
37 #include "Types.h"
38 
39 #ifndef NN_COMPATIBILITY_LIBRARY_BUILD
40 #include <android/hidl/allocator/1.0/IAllocator.h>
41 #include <hidl/HidlSupport.h>
42 #include <hidlmemory/mapping.h>
43 #include <sys/mman.h>
44 #else
45 #include "DynamicCLDeps.h"
46 #endif  // NN_COMPATIBILITY_LIBRARY_BUILD
47 
48 namespace android::nn {
49 namespace {
50 
createSharedMemoryFromUniqueFd(size_t size,int prot,base::unique_fd fd,size_t offset)51 GeneralResult<SharedMemory> createSharedMemoryFromUniqueFd(size_t size, int prot,
52                                                            base::unique_fd fd, size_t offset) {
53     auto handle = Memory::Fd{
54             .size = size,
55             .prot = prot,
56             .fd = std::move(fd),
57             .offset = offset,
58     };
59     return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
60 }
61 
62 #ifndef NN_COMPATIBILITY_LIBRARY_BUILD
63 
64 using ::android::hardware::hidl_memory;
65 using ::android::hidl::allocator::V1_0::IAllocator;
66 
67 const char* const kAllocatorService = "ashmem";
68 
hidlHandleFromUniqueFd(base::unique_fd fd)69 GeneralResult<hardware::hidl_handle> hidlHandleFromUniqueFd(base::unique_fd fd) {
70     native_handle_t* nativeHandle = native_handle_create(1, 0);
71     if (nativeHandle == nullptr) {
72         return NN_ERROR() << "Failed to create native_handle";
73     }
74     nativeHandle->data[0] = fd.release();
75 
76     hardware::hidl_handle hidlHandle;
77     hidlHandle.setTo(nativeHandle, /*shouldOwn=*/true);
78     return hidlHandle;
79 }
80 
allocateSharedMemory(size_t size)81 GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
82     static const auto allocator = IAllocator::getService(kAllocatorService);
83     CHECK_GT(size, 0u);
84 
85     hidl_memory maybeMemory;
86     auto fn = [&maybeMemory](bool success, const hidl_memory& memory) {
87         if (success) {
88             maybeMemory = memory;
89         }
90     };
91     allocator->allocate(size, fn);
92 
93     if (!maybeMemory.valid()) {
94         return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
95                << "IAllocator::allocate returned an invalid (empty) memory object";
96     }
97     if (maybeMemory.handle()->numFds != 1) {
98         return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
99                           << maybeMemory.handle()->numFds << " numFds, but expected 1";
100     }
101     if (maybeMemory.handle()->numInts != 0) {
102         return NN_ERROR() << "IAllocator::allocate returned an invalid memory object with "
103                           << maybeMemory.handle()->numInts << " numInts, but expected 0";
104     }
105 
106     CHECK_LE(maybeMemory.size(), std::numeric_limits<size_t>::max());
107     const int fd = maybeMemory.handle()->data[0];
108 
109     auto handle = Memory::Ashmem{
110             .fd = NN_TRY(dupFd(fd)),
111             .size = static_cast<size_t>(maybeMemory.size()),
112     };
113     return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
114 }
115 
map(const Memory::Ashmem & memory)116 GeneralResult<Mapping> map(const Memory::Ashmem& memory) {
117     auto handle = NN_TRY(hidlHandleFromUniqueFd(NN_TRY(dupFd(memory.fd))));
118     const auto hidlMemory = hidl_memory("ashmem", std::move(handle), memory.size);
119 
120     const auto mapping = mapMemory(hidlMemory);
121     if (mapping == nullptr) {
122         return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to map memory";
123     }
124 
125     auto* const pointer = mapping->getPointer().withDefault(nullptr);
126     if (pointer == nullptr) {
127         return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped pointer";
128     }
129 
130     const auto fullSize = mapping->getSize().withDefault(0);
131     if (fullSize == 0 || fullSize > std::numeric_limits<size_t>::max()) {
132         return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to get the mapped size";
133     }
134 
135     const size_t size = static_cast<size_t>(fullSize);
136 
137     return Mapping{
138             .pointer = pointer,
139             .size = size,
140             .context = mapping,
141     };
142 }
143 
144 #else
145 
allocateSharedMemory(size_t size)146 GeneralResult<SharedMemory> allocateSharedMemory(size_t size) {
147     CHECK_GT(size, 0u);
148 
149     const CompatibilityLayerMemory& memory = loadCompatibilityLayerMemory();
150     auto fd = base::unique_fd(memory.create(nullptr, size));
151     if (!fd.ok()) {
152         return NN_ERROR() << "ASharedMemory_create failed";
153     }
154 
155     const size_t readSize = memory.getSize(fd.get());
156     CHECK_GE(readSize, size);
157 
158     constexpr int prot = PROT_READ | PROT_WRITE;
159     constexpr size_t offset = 0;
160     return createSharedMemoryFromUniqueFd(size, prot, std::move(fd), offset);
161 }
162 
map(const Memory::Ashmem &)163 GeneralResult<Mapping> map(const Memory::Ashmem& /*memory*/) {
164     return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map ashmem memory";
165 }
166 
167 #endif  // NN_COMPATIBILITY_LIBRARY_BUILD
168 
getSize(const Memory::Ashmem & memory)169 size_t getSize(const Memory::Ashmem& memory) {
170     return memory.size;
171 }
172 
getSize(const Memory::Fd & memory)173 size_t getSize(const Memory::Fd& memory) {
174     return memory.size;
175 }
176 
getSize(const Memory::HardwareBuffer & memory)177 size_t getSize(const Memory::HardwareBuffer& memory) {
178     AHardwareBuffer_Desc desc;
179     AHardwareBuffer_describe(memory.handle.get(), &desc);
180     return desc.format == AHARDWAREBUFFER_FORMAT_BLOB ? desc.width : 0;
181 }
182 
getSize(const Memory::Unknown & memory)183 size_t getSize(const Memory::Unknown& memory) {
184     return memory.size;
185 }
186 
187 struct MmapFdMappingContext {
188     int prot;
189     std::any context;
190 };
191 
map(const Memory::Fd & memory)192 GeneralResult<Mapping> map(const Memory::Fd& memory) {
193     std::shared_ptr<base::MappedFile> mapping =
194             base::MappedFile::FromFd(memory.fd, memory.offset, memory.size, memory.prot);
195     if (mapping == nullptr) {
196         return NN_ERROR() << "Can't mmap the file descriptor.";
197     }
198     char* data = mapping->data();
199 
200     const bool writable = (memory.prot & PROT_WRITE) != 0;
201     std::variant<const void*, void*> pointer;
202     if (writable) {
203         pointer = static_cast<void*>(data);
204     } else {
205         pointer = static_cast<const void*>(data);
206     }
207 
208     auto context = MmapFdMappingContext{.prot = memory.prot, .context = std::move(mapping)};
209     return Mapping{.pointer = pointer, .size = memory.size, .context = std::move(context)};
210 }
211 
map(const Memory::HardwareBuffer & memory)212 GeneralResult<Mapping> map(const Memory::HardwareBuffer& memory) {
213     AHardwareBuffer_Desc desc;
214     AHardwareBuffer_describe(memory.handle.get(), &desc);
215 
216     if (desc.format != AHARDWAREBUFFER_FORMAT_BLOB) {
217         return NN_ERROR() << "Unable to map non-blob AHardwareBuffer memory";
218     }
219     const uint32_t size = desc.width;
220 
221     const uint64_t kCpuUsageMask =
222             AHARDWAREBUFFER_USAGE_CPU_READ_MASK | AHARDWAREBUFFER_USAGE_CPU_WRITE_MASK;
223     void* data = nullptr;
224     const auto status = AHardwareBuffer_lock(memory.handle.get(), desc.usage & kCpuUsageMask, -1,
225                                              nullptr, &data);
226     if (status != /*NO_ERROR*/ 0) {
227         return NN_ERROR() << "Can't lock the AHardwareBuffer. Error: " << status;
228     }
229 
230     // Create shared scoped object to munmap.
231     auto scoped = base::make_scope_guard(
232             [ahwb = memory.handle.get()] { AHardwareBuffer_unlock(ahwb, nullptr); });
233     auto sharedScoped = std::make_shared<decltype(scoped)>(std::move(scoped));
234 
235     return Mapping{.pointer = data, .size = size, .context = std::move(sharedScoped)};
236 }
237 
map(const Memory::Unknown &)238 GeneralResult<Mapping> map(const Memory::Unknown& /*memory*/) {
239     return NN_ERROR(ErrorStatus::INVALID_ARGUMENT) << "Cannot map Unknown memory";
240 }
241 
freeHardwareBuffer(AHardwareBuffer * buffer)242 void freeHardwareBuffer(AHardwareBuffer* buffer) {
243     if (buffer) {
244         AHardwareBuffer_release(buffer);
245     }
246 }
247 
freeNoop(AHardwareBuffer *)248 void freeNoop(AHardwareBuffer* /*buffer*/) {}
249 
250 }  // namespace
251 
dupFd(int fd)252 GeneralResult<base::unique_fd> dupFd(int fd) {
253     if (fd < 0) {
254         return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "dupFd was passed an invalid fd";
255     }
256     auto uniqueFd = base::unique_fd(dup(fd));
257     if (!uniqueFd.ok()) {
258         // TODO(b/120417090): is ANEURALNETWORKS_UNEXPECTED_NULL the correct error to return here?
259         return NN_ERROR(ErrorStatus::GENERAL_FAILURE) << "Failed to dup the fd";
260     }
261     return uniqueFd;
262 }
263 
createSharedMemory(size_t size)264 GeneralResult<SharedMemory> createSharedMemory(size_t size) {
265     return allocateSharedMemory(size);
266 }
267 
createSharedMemoryFromFd(size_t size,int prot,int fd,size_t offset)268 GeneralResult<SharedMemory> createSharedMemoryFromFd(size_t size, int prot, int fd, size_t offset) {
269     return createSharedMemoryFromUniqueFd(size, prot, NN_TRY(dupFd(fd)), offset);
270 }
271 
createSharedMemoryFromAHWB(AHardwareBuffer * ahwb,bool takeOwnership)272 GeneralResult<SharedMemory> createSharedMemoryFromAHWB(AHardwareBuffer* ahwb, bool takeOwnership) {
273     CHECK(ahwb != nullptr);
274     const Memory::HardwareBuffer::Deleter deleter = (takeOwnership ? freeHardwareBuffer : freeNoop);
275     Memory::HardwareBuffer handle = {.handle = Memory::HardwareBuffer::Handle(ahwb, deleter)};
276     return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
277 }
278 
getSize(const SharedMemory & memory)279 size_t getSize(const SharedMemory& memory) {
280     CHECK(memory != nullptr);
281     return std::visit([](const auto& x) { return getSize(x); }, memory->handle);
282 }
283 
isAhwbBlob(const Memory::HardwareBuffer & memory)284 bool isAhwbBlob(const Memory::HardwareBuffer& memory) {
285     AHardwareBuffer* ahwb = memory.handle.get();
286     AHardwareBuffer_Desc desc;
287     AHardwareBuffer_describe(ahwb, &desc);
288     return desc.format == AHARDWAREBUFFER_FORMAT_BLOB;
289 }
290 
isAhwbBlob(const SharedMemory & memory)291 bool isAhwbBlob(const SharedMemory& memory) {
292     CHECK(memory != nullptr);
293     if (!std::holds_alternative<Memory::HardwareBuffer>(memory->handle)) {
294         return false;
295     }
296     return isAhwbBlob(std::get<Memory::HardwareBuffer>(memory->handle));
297 }
298 
map(const SharedMemory & memory)299 GeneralResult<Mapping> map(const SharedMemory& memory) {
300     if (memory == nullptr) {
301         return NN_ERROR() << "Unable to map nullptr SharedMemory object";
302     }
303     return std::visit([](const auto& x) { return map(x); }, memory->handle);
304 }
305 
flush(const Mapping & mapping)306 bool flush(const Mapping& mapping) {
307     if (const auto* mmapFdMapping = std::any_cast<MmapFdMappingContext>(&mapping.context)) {
308         if (!std::holds_alternative<void*>(mapping.pointer)) {
309             return true;
310         }
311         void* data = std::get<void*>(mapping.pointer);
312         const int prot = mmapFdMapping->prot;
313         if (prot & PROT_WRITE) {
314             const size_t size = mapping.size;
315             return msync(data, size, MS_SYNC) == 0;
316         }
317     }
318     // No-op for other types of memory.
319     return true;
320 }
321 
322 }  // namespace android::nn
323