blob: bbc59c618d539949dc5769805ab3bc9d446d9f52 [file] [log] [blame]
Lingfeng Yanga963ea02019-03-21 21:27:04 -07001// 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
Luca Stefani1cb647a2019-03-07 21:58:17 +010016#if PLATFORM_SDK_VERSION < 26
Roman Kiryanov8f9ed182018-12-06 10:31:24 -080017#include <cutils/log.h>
Luca Stefani1cb647a2019-03-07 21:58:17 +010018#else
19#include <log/log.h>
20#endif
21
Roman Kiryanovdaecd142018-11-14 14:56:27 -080022#include "goldfish_address_space.h"
23
Lingfeng Yangc08fbfb2018-12-05 21:05:17 -080024#ifdef HOST_BUILD
Lingfeng Yanga963ea02019-03-21 21:27:04 -070025
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.
31class AddressSpaceHost {
32public:
33 AddressSpaceHost() : mAlloc(0, 16ULL * 1024ULL * 1048576ULL, 4096) { }
34 uint64_t alloc(size_t size) {
35 return (uint64_t)(uintptr_t)mAlloc.alloc(size);
36 }
37 void free(uint64_t addr) {
38 mAlloc.free((void*)(uintptr_t)addr);
39 }
40private:
41 android::base::SubAllocator mAlloc;
42};
43
44static 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).
51GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider()
52 : mAlloc(sAddressSpaceHost.ptr()) {}
53
54GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider() { }
55
56uint64_t GoldfishAddressSpaceBlockProvider::allocPhys(size_t size) {
57 AddressSpaceHost* hostAlloc = reinterpret_cast<AddressSpaceHost*>(mAlloc);
58 return hostAlloc->alloc(size);
59}
60
61void GoldfishAddressSpaceBlockProvider::freePhys(uint64_t phys) {
62 AddressSpaceHost* hostAlloc = reinterpret_cast<AddressSpaceHost*>(mAlloc);
63 return hostAlloc->free(phys);
64}
65
66GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock() :
67 m_alloced(false), m_guest_ptr(NULL), m_phys_addr(0), m_provider(NULL) {}
68GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock() { destroy(); }
69
70GoldfishAddressSpaceBlock &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}
Roman Kiryanovdaecd142018-11-14 14:56:27 -080077
78bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
79{
Lingfeng Yanga963ea02019-03-21 21:27:04 -070080 destroy();
81 m_phys_addr = provider->allocPhys(size);
82 m_alloced = true;
83 m_provider = provider;
Roman Kiryanovdaecd142018-11-14 14:56:27 -080084 return true;
85}
86
87uint64_t GoldfishAddressSpaceBlock::physAddr() const
88{
Lingfeng Yanga963ea02019-03-21 21:27:04 -070089 return m_phys_addr;
Roman Kiryanovdaecd142018-11-14 14:56:27 -080090}
91
92uint64_t GoldfishAddressSpaceBlock::hostAddr() const
93{
94 return 42; // some random number, not used
95}
96
97void *GoldfishAddressSpaceBlock::mmap(uint64_t opaque)
98{
99 m_guest_ptr = reinterpret_cast<void *>(opaque);
100 return m_guest_ptr;
101}
102
103void *GoldfishAddressSpaceBlock::guestPtr() const
104{
105 return m_guest_ptr;
106}
107
108void GoldfishAddressSpaceBlock::destroy()
109{
Lingfeng Yanga963ea02019-03-21 21:27:04 -0700110 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 }
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800117}
118
119void 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}
David Reveman5b7c5842019-02-20 01:06:48 -0500128#elif __Fuchsia__
129#include <fcntl.h>
David Reveman397f5682019-04-08 11:30:05 -0400130#include <fuchsia/hardware/goldfish/address/space/c/fidl.h>
131#include <lib/fdio/fdio.h>
132#include <stdlib.h>
David Reveman5b7c5842019-02-20 01:06:48 -0500133#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
David Reveman397f5682019-04-08 11:30:05 -0400140GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider() {
141 fdio_get_service_handle(::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR), &m_channel);
142}
David Reveman5b7c5842019-02-20 01:06:48 -0500143
144GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
145{
David Reveman397f5682019-04-08 11:30:05 -0400146 zx_handle_close(m_channel);
David Reveman5b7c5842019-02-20 01:06:48 -0500147}
148
149GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
150 : m_vmo(ZX_HANDLE_INVALID)
David Reveman397f5682019-04-08 11:30:05 -0400151 , m_channel(ZX_HANDLE_INVALID)
David Reveman5b7c5842019-02-20 01:06:48 -0500152 , m_mmaped_ptr(NULL)
153 , m_phys_addr(0)
154 , m_host_addr(0)
155 , m_offset(0)
David Reveman397f5682019-04-08 11:30:05 -0400156 , m_size(0) {}
David Reveman5b7c5842019-02-20 01:06:48 -0500157
158GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
159{
160 destroy();
161}
162
163GoldfishAddressSpaceBlock &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;
David Reveman397f5682019-04-08 11:30:05 -0400171 m_channel = rhs.m_channel;
David Reveman5b7c5842019-02-20 01:06:48 -0500172
173 return *this;
174}
175
176bool 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
David Reveman397f5682019-04-08 11:30:05 -0400187 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;
David Reveman5b7c5842019-02-20 01:06:48 -0500194 }
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
David Reveman397f5682019-04-08 11:30:05 -0400203 m_channel = provider->m_channel;
David Reveman5b7c5842019-02-20 01:06:48 -0500204 return true;
205}
206
207uint64_t GoldfishAddressSpaceBlock::physAddr() const
208{
209 return m_phys_addr;
210}
211
212uint64_t GoldfishAddressSpaceBlock::hostAddr() const
213{
214 return m_host_addr;
215}
216
217void *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
249void *GoldfishAddressSpaceBlock::guestPtr() const
250{
251 return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
252}
253
254void 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;
David Reveman397f5682019-04-08 11:30:05 -0400266 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;
David Reveman5b7c5842019-02-20 01:06:48 -0500274 m_phys_addr = 0;
275 m_host_addr = 0;
276 m_offset = 0;
277 m_size = 0;
278 }
279}
280
281void GoldfishAddressSpaceBlock::replace(GoldfishAddressSpaceBlock *other)
282{
283 destroy();
284
285 if (other) {
286 *this = *other;
287 *other = GoldfishAddressSpaceBlock();
288 }
289}
290
291bool GoldfishAddressSpaceBlockProvider::is_opened()
292{
David Reveman397f5682019-04-08 11:30:05 -0400293 return m_channel != ZX_HANDLE_INVALID;
David Reveman5b7c5842019-02-20 01:06:48 -0500294}
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800295#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
307struct 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
318const char GOLDFISH_ADDRESS_SPACE_DEVICE_NAME[] = "/dev/goldfish_address_space";
319
320GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider()
321 : m_fd(::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR)) {}
322
323GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
324{
325 ::close(m_fd);
326}
327
328GoldfishAddressSpaceBlock::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
336GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
337{
338 destroy();
339}
340
341GoldfishAddressSpaceBlock &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
353bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
354{
Lingfeng Yang49c7de22019-01-23 16:19:50 -0800355
356 ALOGD("%s: Ask for block of size 0x%llx\n", __func__,
357 (unsigned long long)size);
358
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800359 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;
Lingfeng Yang49c7de22019-01-23 16:19:50 -0800374
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800375 m_offset = request.offset;
376 m_size = request.size;
Lingfeng Yang49c7de22019-01-23 16:19:50 -0800377
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
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800382 m_fd = provider->m_fd;
383 return true;
384 }
385}
386
387uint64_t GoldfishAddressSpaceBlock::physAddr() const
388{
389 return m_phys_addr;
390}
391
392uint64_t GoldfishAddressSpaceBlock::hostAddr() const
393{
394 return m_host_addr;
395}
396
397void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
398{
399 if (m_size == 0) {
Lingfeng Yang49c7de22019-01-23 16:19:50 -0800400 ALOGE("%s: called with zero size\n", __func__);
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800401 return NULL;
402 }
403 if (m_mmaped_ptr) {
404 ALOGE("'mmap' called for an already mmaped address block");
405 ::abort();
406 }
407
Lingfeng Yang49c7de22019-01-23 16:19:50 -0800408 void *result = ::mmap64(NULL, m_size, PROT_WRITE, MAP_SHARED, m_fd, m_offset);
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800409 if (result == MAP_FAILED) {
Lingfeng Yang49c7de22019-01-23 16:19:50 -0800410 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);
Roman Kiryanovdaecd142018-11-14 14:56:27 -0800415 return NULL;
416 } else {
417 m_mmaped_ptr = result;
418 m_host_addr = host_addr;
419 return guestPtr();
420 }
421}
422
423void *GoldfishAddressSpaceBlock::guestPtr() const
424{
425 return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
426}
427
428void 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
444void GoldfishAddressSpaceBlock::replace(GoldfishAddressSpaceBlock *other)
445{
446 destroy();
447
448 if (other) {
449 *this = *other;
450 *other = GoldfishAddressSpaceBlock();
451 }
452}
453
454bool GoldfishAddressSpaceBlockProvider::is_opened()
455{
456 return m_fd >= 0;
457}
458#endif