1 // Copyright (C) 2019 The Android Open Source Project
2 // Copyright (C) 2019 Google Inc.
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 #if PLATFORM_SDK_VERSION < 26
17 #include <cutils/log.h>
18 #else
19 #include <log/log.h>
20 #endif
21
22 #include "goldfish_address_space.h"
23
24 #ifdef HOST_BUILD
25
26 #include "android/base/SubAllocator.h"
27 #include "android/base/memory/LazyInstance.h"
28
29 // AddressSpaceHost is a global class for obtaining physical addresses
30 // for the host-side build.
31 class AddressSpaceHost {
32 public:
AddressSpaceHost()33 AddressSpaceHost() : mAlloc(0, 16ULL * 1024ULL * 1048576ULL, 4096) { }
alloc(size_t size)34 uint64_t alloc(size_t size) {
35 return (uint64_t)(uintptr_t)mAlloc.alloc(size);
36 }
free(uint64_t addr)37 void free(uint64_t addr) {
38 mAlloc.free((void*)(uintptr_t)addr);
39 }
40 private:
41 android::base::SubAllocator mAlloc;
42 };
43
44 static android::base::LazyInstance<AddressSpaceHost> sAddressSpaceHost =
45 LAZY_INSTANCE_INIT;
46
47 // It may seem like there should be one allocator per provider,
48 // but to properly reflect the guest behavior where there is only one
49 // allocator in the system and multiple providers are possible,
50 // we need to have a separate global object (sAddressSpaceHost).
GoldfishAddressSpaceBlockProvider()51 GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider()
52 : mAlloc(sAddressSpaceHost.ptr()) {}
53
~GoldfishAddressSpaceBlockProvider()54 GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider() { }
55
allocPhys(size_t size)56 uint64_t GoldfishAddressSpaceBlockProvider::allocPhys(size_t size) {
57 AddressSpaceHost* hostAlloc = reinterpret_cast<AddressSpaceHost*>(mAlloc);
58 return hostAlloc->alloc(size);
59 }
60
freePhys(uint64_t phys)61 void GoldfishAddressSpaceBlockProvider::freePhys(uint64_t phys) {
62 AddressSpaceHost* hostAlloc = reinterpret_cast<AddressSpaceHost*>(mAlloc);
63 return hostAlloc->free(phys);
64 }
65
GoldfishAddressSpaceBlock()66 GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock() :
67 m_alloced(false), m_guest_ptr(NULL), m_phys_addr(0), m_provider(NULL) {}
~GoldfishAddressSpaceBlock()68 GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock() { destroy(); }
69
operator =(const GoldfishAddressSpaceBlock & rhs)70 GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
71 {
72 m_guest_ptr = rhs.m_guest_ptr;
73 m_phys_addr = rhs.m_phys_addr;
74 m_provider = rhs.m_provider;
75 return *this;
76 }
77
allocate(GoldfishAddressSpaceBlockProvider * provider,size_t size)78 bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
79 {
80 destroy();
81 m_phys_addr = provider->allocPhys(size);
82 m_alloced = true;
83 m_provider = provider;
84 return true;
85 }
86
physAddr() const87 uint64_t GoldfishAddressSpaceBlock::physAddr() const
88 {
89 return m_phys_addr;
90 }
91
hostAddr() const92 uint64_t GoldfishAddressSpaceBlock::hostAddr() const
93 {
94 return 42; // some random number, not used
95 }
96
mmap(uint64_t opaque)97 void *GoldfishAddressSpaceBlock::mmap(uint64_t opaque)
98 {
99 m_guest_ptr = reinterpret_cast<void *>(opaque);
100 return m_guest_ptr;
101 }
102
guestPtr() const103 void *GoldfishAddressSpaceBlock::guestPtr() const
104 {
105 return m_guest_ptr;
106 }
107
destroy()108 void GoldfishAddressSpaceBlock::destroy()
109 {
110 if (m_alloced) {
111 m_guest_ptr = NULL;
112 if (m_provider) {
113 m_provider->freePhys(m_phys_addr);
114 }
115 m_alloced = false;
116 }
117 }
118
replace(GoldfishAddressSpaceBlock * other)119 void GoldfishAddressSpaceBlock::replace(GoldfishAddressSpaceBlock *other)
120 {
121 if (other) {
122 this->m_guest_ptr = other->m_guest_ptr;
123 other->m_guest_ptr = NULL;
124 } else {
125 this->m_guest_ptr = NULL;
126 }
127 }
128 #elif __Fuchsia__
129 #include <fcntl.h>
130 #include <fuchsia/hardware/goldfish/address/space/c/fidl.h>
131 #include <lib/fdio/fdio.h>
132 #include <stdlib.h>
133 #include <sys/stat.h>
134 #include <sys/types.h>
135 #include <unistd.h>
136 #include <zircon/process.h>
137 #include <zircon/syscalls.h>
138 #include <zircon/syscalls/object.h>
139
GoldfishAddressSpaceBlockProvider()140 GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider() {
141 fdio_get_service_handle(::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR), &m_channel);
142 }
143
~GoldfishAddressSpaceBlockProvider()144 GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
145 {
146 zx_handle_close(m_channel);
147 }
148
GoldfishAddressSpaceBlock()149 GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
150 : m_vmo(ZX_HANDLE_INVALID)
151 , m_channel(ZX_HANDLE_INVALID)
152 , m_mmaped_ptr(NULL)
153 , m_phys_addr(0)
154 , m_host_addr(0)
155 , m_offset(0)
156 , m_size(0) {}
157
~GoldfishAddressSpaceBlock()158 GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
159 {
160 destroy();
161 }
162
operator =(const GoldfishAddressSpaceBlock & rhs)163 GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
164 {
165 m_vmo = rhs.m_vmo;
166 m_mmaped_ptr = rhs.m_mmaped_ptr;
167 m_phys_addr = rhs.m_phys_addr;
168 m_host_addr = rhs.m_host_addr;
169 m_offset = rhs.m_offset;
170 m_size = rhs.m_size;
171 m_channel = rhs.m_channel;
172
173 return *this;
174 }
175
allocate(GoldfishAddressSpaceBlockProvider * provider,size_t size)176 bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
177 {
178 ALOGD("%s: Ask for block of size 0x%llx\n", __func__,
179 (unsigned long long)size);
180
181 destroy();
182
183 if (!provider->is_opened()) {
184 return false;
185 }
186
187 int32_t res = ZX_OK;
188 zx_status_t status =
189 fuchsia_hardware_goldfish_address_space_DeviceAllocateBlock(
190 provider->m_channel, size, &res, &m_phys_addr, &m_vmo);
191 if (status != ZX_OK || res != ZX_OK) {
192 ALOGE("%s: allocate block failed: %d:%d", __func__, status, res);
193 return false;
194 }
195
196 m_offset = 0;
197 m_size = size;
198
199 ALOGD("%s: allocate returned offset 0x%llx size 0x%llx\n", __func__,
200 (unsigned long long)m_offset,
201 (unsigned long long)m_size);
202
203 m_channel = provider->m_channel;
204 return true;
205 }
206
physAddr() const207 uint64_t GoldfishAddressSpaceBlock::physAddr() const
208 {
209 return m_phys_addr;
210 }
211
hostAddr() const212 uint64_t GoldfishAddressSpaceBlock::hostAddr() const
213 {
214 return m_host_addr;
215 }
216
mmap(uint64_t host_addr)217 void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
218 {
219 if (m_size == 0) {
220 ALOGE("%s: called with zero size\n", __func__);
221 return NULL;
222 }
223 if (m_mmaped_ptr) {
224 ALOGE("'mmap' called for an already mmaped address block");
225 ::abort();
226 }
227
228 zx_vaddr_t ptr = 0;
229 zx_status_t status = zx_vmar_map(zx_vmar_root_self(),
230 ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
231 0, m_vmo,
232 m_offset,
233 m_size,
234 &ptr);
235 if (status != ZX_OK) {
236 ALOGE("%s: host memory map failed with size 0x%llx "
237 "off 0x%llx status %d\n",
238 __func__,
239 (unsigned long long)m_size,
240 (unsigned long long)m_offset, status);
241 return NULL;
242 } else {
243 m_mmaped_ptr = (void*)ptr;
244 m_host_addr = host_addr;
245 return guestPtr();
246 }
247 }
248
guestPtr() const249 void *GoldfishAddressSpaceBlock::guestPtr() const
250 {
251 return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
252 }
253
destroy()254 void GoldfishAddressSpaceBlock::destroy()
255 {
256 if (m_mmaped_ptr && m_size) {
257 zx_vmar_unmap(zx_vmar_root_self(),
258 (zx_vaddr_t)m_mmaped_ptr,
259 m_size);
260 m_mmaped_ptr = NULL;
261 }
262
263 if (m_size) {
264 zx_handle_close(m_vmo);
265 m_vmo = ZX_HANDLE_INVALID;
266 int32_t res = ZX_OK;
267 zx_status_t status =
268 fuchsia_hardware_goldfish_address_space_DeviceDeallocateBlock(
269 m_channel, m_phys_addr, &res);
270 if (status != ZX_OK || res != ZX_OK) {
271 ALOGE("%s: deallocate block failed: %d:%d", __func__, status, res);
272 }
273 m_channel = ZX_HANDLE_INVALID;
274 m_phys_addr = 0;
275 m_host_addr = 0;
276 m_offset = 0;
277 m_size = 0;
278 }
279 }
280
replace(GoldfishAddressSpaceBlock * other)281 void GoldfishAddressSpaceBlock::replace(GoldfishAddressSpaceBlock *other)
282 {
283 destroy();
284
285 if (other) {
286 *this = *other;
287 *other = GoldfishAddressSpaceBlock();
288 }
289 }
290
is_opened()291 bool GoldfishAddressSpaceBlockProvider::is_opened()
292 {
293 return m_channel != ZX_HANDLE_INVALID;
294 }
295 #else
296 #include <linux/types.h>
297 #include <linux/ioctl.h>
298 #include <sys/types.h>
299 #include <sys/stat.h>
300 #include <sys/mman.h>
301 #include <sys/ioctl.h>
302 #include <fcntl.h>
303 #include <unistd.h>
304 #include <cstdlib>
305 #include <errno.h>
306
307 struct goldfish_address_space_allocate_block {
308 __u64 size;
309 __u64 offset;
310 __u64 phys_addr;
311 };
312
313 #define GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC 'G'
314 #define GOLDFISH_ADDRESS_SPACE_IOCTL_OP(OP, T) _IOWR(GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC, OP, T)
315 #define GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(10, struct goldfish_address_space_allocate_block)
316 #define GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(11, __u64)
317
318 const char GOLDFISH_ADDRESS_SPACE_DEVICE_NAME[] = "/dev/goldfish_address_space";
319
GoldfishAddressSpaceBlockProvider()320 GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider()
321 : m_fd(::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR)) {}
322
~GoldfishAddressSpaceBlockProvider()323 GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
324 {
325 ::close(m_fd);
326 }
327
GoldfishAddressSpaceBlock()328 GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
329 : m_mmaped_ptr(NULL)
330 , m_phys_addr(0)
331 , m_host_addr(0)
332 , m_offset(0)
333 , m_size(0)
334 , m_fd(-1) {}
335
~GoldfishAddressSpaceBlock()336 GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
337 {
338 destroy();
339 }
340
operator =(const GoldfishAddressSpaceBlock & rhs)341 GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
342 {
343 m_mmaped_ptr = rhs.m_mmaped_ptr;
344 m_phys_addr = rhs.m_phys_addr;
345 m_host_addr = rhs.m_host_addr;
346 m_offset = rhs.m_offset;
347 m_size = rhs.m_size;
348 m_fd = rhs.m_fd;
349
350 return *this;
351 }
352
allocate(GoldfishAddressSpaceBlockProvider * provider,size_t size)353 bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
354 {
355
356 ALOGD("%s: Ask for block of size 0x%llx\n", __func__,
357 (unsigned long long)size);
358
359 destroy();
360
361 if (!provider->is_opened()) {
362 return false;
363 }
364
365 struct goldfish_address_space_allocate_block request;
366 long res;
367
368 request.size = size;
369 res = ::ioctl(provider->m_fd, GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK, &request);
370 if (res) {
371 return false;
372 } else {
373 m_phys_addr = request.phys_addr;
374
375 m_offset = request.offset;
376 m_size = request.size;
377
378 ALOGD("%s: ioctl allocate returned offset 0x%llx size 0x%llx\n", __func__,
379 (unsigned long long)m_offset,
380 (unsigned long long)m_size);
381
382 m_fd = provider->m_fd;
383 return true;
384 }
385 }
386
physAddr() const387 uint64_t GoldfishAddressSpaceBlock::physAddr() const
388 {
389 return m_phys_addr;
390 }
391
hostAddr() const392 uint64_t GoldfishAddressSpaceBlock::hostAddr() const
393 {
394 return m_host_addr;
395 }
396
mmap(uint64_t host_addr)397 void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
398 {
399 if (m_size == 0) {
400 ALOGE("%s: called with zero size\n", __func__);
401 return NULL;
402 }
403 if (m_mmaped_ptr) {
404 ALOGE("'mmap' called for an already mmaped address block");
405 ::abort();
406 }
407
408 void *result = ::mmap64(NULL, m_size, PROT_WRITE, MAP_SHARED, m_fd, m_offset);
409 if (result == MAP_FAILED) {
410 ALOGE("%s: host memory map failed with size 0x%llx "
411 "off 0x%llx errno %d\n",
412 __func__,
413 (unsigned long long)m_size,
414 (unsigned long long)m_offset, errno);
415 return NULL;
416 } else {
417 m_mmaped_ptr = result;
418 m_host_addr = host_addr;
419 return guestPtr();
420 }
421 }
422
guestPtr() const423 void *GoldfishAddressSpaceBlock::guestPtr() const
424 {
425 return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
426 }
427
destroy()428 void GoldfishAddressSpaceBlock::destroy()
429 {
430 if (m_mmaped_ptr && m_size) {
431 ::munmap(m_mmaped_ptr, m_size);
432 m_mmaped_ptr = NULL;
433 }
434
435 if (m_size) {
436 ::ioctl(m_fd, GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK, &m_offset);
437 m_phys_addr = 0;
438 m_host_addr = 0;
439 m_offset = 0;
440 m_size = 0;
441 }
442 }
443
replace(GoldfishAddressSpaceBlock * other)444 void GoldfishAddressSpaceBlock::replace(GoldfishAddressSpaceBlock *other)
445 {
446 destroy();
447
448 if (other) {
449 *this = *other;
450 *other = GoldfishAddressSpaceBlock();
451 }
452 }
453
is_opened()454 bool GoldfishAddressSpaceBlockProvider::is_opened()
455 {
456 return m_fd >= 0;
457 }
458 #endif
459