• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#include <linux/types.h>
17#include <linux/ioctl.h>
18#include <sys/types.h>
19#include <sys/stat.h>
20#include <sys/mman.h>
21#include <sys/ioctl.h>
22#include <fcntl.h>
23#include <unistd.h>
24#include <cstdlib>
25#include <errno.h>
26#include <memory>
27
28#include <log/log.h>
29
30#include "goldfish_address_space.h"
31
32namespace {
33
34struct goldfish_address_space_allocate_block {
35    __u64 size;
36    __u64 offset;
37    __u64 phys_addr;
38};
39
40struct goldfish_address_space_claim_shared {
41    __u64 offset;
42    __u64 size;
43};
44
45#define GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC		'G'
46#define GOLDFISH_ADDRESS_SPACE_IOCTL_OP(OP, T)		_IOWR(GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC, OP, T)
47#define GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK	GOLDFISH_ADDRESS_SPACE_IOCTL_OP(10, struct goldfish_address_space_allocate_block)
48#define GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK	GOLDFISH_ADDRESS_SPACE_IOCTL_OP(11, __u64)
49#define GOLDFISH_ADDRESS_SPACE_IOCTL_PING		GOLDFISH_ADDRESS_SPACE_IOCTL_OP(12, struct goldfish_address_space_ping)
50#define GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED		GOLDFISH_ADDRESS_SPACE_IOCTL_OP(13, struct goldfish_address_space_claim_shared)
51#define GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED		GOLDFISH_ADDRESS_SPACE_IOCTL_OP(14, __u64)
52
53const char GOLDFISH_ADDRESS_SPACE_DEVICE_NAME[] = "/dev/goldfish_address_space";
54
55const int HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID = 1;
56const int HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID = 2;
57
58int create_address_space_fd()
59{
60    return ::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR);
61}
62
63long ioctl_allocate(int fd, struct goldfish_address_space_allocate_block *request)
64{
65    return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK, request);
66}
67
68long ioctl_deallocate(int fd, uint64_t offset)
69{
70    return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK, &offset);
71}
72
73long ioctl_ping(int fd, struct goldfish_address_space_ping *request)
74{
75    return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_PING, request);
76}
77
78long set_address_space_subdevice_type(int fd, uint64_t type)
79{
80    struct goldfish_address_space_ping request;
81    ::memset(&request, 0, sizeof(request));
82    request.version = sizeof(request);
83    request.metadata = type;
84
85    long ret = ioctl_ping(fd, &request);
86    if (ret) {
87        return ret;
88    }
89
90    return request.metadata;
91}
92
93long ioctl_claim_shared(int fd, struct goldfish_address_space_claim_shared *request)
94{
95    return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED, request);
96}
97
98long ioctl_unclaim_shared(int fd, uint64_t offset)
99{
100    return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED, &offset);
101}
102
103}  // namespace
104
105GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice)
106  : m_handle(create_address_space_fd())
107{
108    if ((subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) && is_opened()) {
109        const long ret = set_address_space_subdevice_type(m_handle, subdevice);
110        if (ret != 0 && ret != subdevice) {  // TODO: retire the 'ret != subdevice' check
111            ALOGE("%s: set_address_space_subdevice_type failed for device_type=%lu, ret=%ld",
112                  __func__, static_cast<unsigned long>(subdevice), ret);
113            close();
114        }
115    }
116}
117
118GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
119{
120    if (is_opened()) {
121        ::close(m_handle);
122    }
123}
124
125bool GoldfishAddressSpaceBlockProvider::is_opened() const
126{
127    return m_handle >= 0;
128}
129
130void GoldfishAddressSpaceBlockProvider::close()
131{
132    if (is_opened()) {
133        ::close(m_handle);
134        m_handle = -1;
135    }
136}
137
138address_space_handle_t GoldfishAddressSpaceBlockProvider::release()
139{
140    address_space_handle_t handle = m_handle;
141    m_handle = -1;
142    return handle;
143}
144
145void GoldfishAddressSpaceBlockProvider::closeHandle(address_space_handle_t handle)
146{
147    ::close(handle);
148}
149
150GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
151    : m_handle(-1)
152    , m_mmaped_ptr(NULL)
153    , m_phys_addr(0)
154    , m_host_addr(0)
155    , m_offset(0)
156    , m_size(0) {}
157
158GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
159{
160    destroy();
161}
162
163GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
164{
165    m_mmaped_ptr = rhs.m_mmaped_ptr;
166    m_phys_addr = rhs.m_phys_addr;
167    m_host_addr = rhs.m_host_addr;
168    m_offset = rhs.m_offset;
169    m_size = rhs.m_size;
170    m_handle = rhs.m_handle;
171
172    return *this;
173}
174
175bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
176{
177    ALOGD("%s: Ask for block of size 0x%llx\n", __func__,
178         (unsigned long long)size);
179
180    destroy();
181
182    if (!provider->is_opened()) {
183        return false;
184    }
185
186    struct goldfish_address_space_allocate_block request;
187    ::memset(&request, 0, sizeof(request));
188    request.size = size;
189
190    long res = ioctl_allocate(provider->m_handle, &request);
191    if (res) {
192        return false;
193    } else {
194        m_phys_addr = request.phys_addr;
195        m_offset = request.offset;
196        m_size = request.size;
197        m_handle = provider->m_handle;
198        m_is_shared_mapping = false;
199
200        ALOGD("%s: ioctl allocate returned offset 0x%llx size 0x%llx\n", __func__,
201                (unsigned long long)m_offset,
202                (unsigned long long)m_size);
203
204        return true;
205    }
206}
207
208bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size)
209{
210    ALOGD("%s: Ask to claim region [0x%llx 0x%llx]\n", __func__,
211         (unsigned long long)offset,
212         (unsigned long long)offset + size);
213
214    destroy();
215
216    if (!provider->is_opened()) {
217        return false;
218    }
219
220    struct goldfish_address_space_claim_shared request;
221    request.offset = offset;
222    request.size = size;
223    long res = ioctl_claim_shared(provider->m_handle, &request);
224
225    if (res) {
226        return false;
227    }
228
229    m_offset = offset;
230    m_size = size;
231    m_handle = provider->m_handle;
232    m_is_shared_mapping = true;
233
234    return true;
235}
236
237uint64_t GoldfishAddressSpaceBlock::physAddr() const
238{
239    return m_phys_addr;
240}
241
242uint64_t GoldfishAddressSpaceBlock::hostAddr() const
243{
244    return m_host_addr;
245}
246
247void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
248{
249    if (m_size == 0) {
250        ALOGE("%s: called with zero size\n", __func__);
251        return NULL;
252    }
253    if (m_mmaped_ptr) {
254        ALOGE("'mmap' called for an already mmaped address block");
255        ::abort();
256    }
257
258    void *result;
259    const int res = memoryMap(NULL, m_size, m_handle, m_offset, &result);
260    if (res) {
261        ALOGE("%s: host memory map failed with size 0x%llx "
262              "off 0x%llx errno %d\n",
263              __func__,
264              (unsigned long long)m_size,
265              (unsigned long long)m_offset, res);
266        return NULL;
267    } else {
268        m_mmaped_ptr = result;
269        m_host_addr = host_addr;
270        return guestPtr();
271    }
272}
273
274void *GoldfishAddressSpaceBlock::guestPtr() const
275{
276    return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
277}
278
279void GoldfishAddressSpaceBlock::destroy()
280{
281    if (m_mmaped_ptr && m_size) {
282        memoryUnmap(m_mmaped_ptr, m_size);
283        m_mmaped_ptr = NULL;
284    }
285
286    if (m_size) {
287        long res = -EINVAL;
288
289        if (m_is_shared_mapping) {
290            res = ioctl_unclaim_shared(m_handle, m_offset);
291            if (res) {
292                ALOGE("ioctl_unclaim_shared failed, res=%ld", res);
293                ::abort();
294            }
295        } else {
296            res = ioctl_deallocate(m_handle, m_offset);
297            if (res) {
298                ALOGE("ioctl_deallocate failed, res=%ld", res);
299                ::abort();
300            }
301        }
302
303        m_is_shared_mapping = false;
304
305        m_phys_addr = 0;
306        m_host_addr = 0;
307        m_offset = 0;
308        m_size = 0;
309    }
310}
311
312void GoldfishAddressSpaceBlock::release()
313{
314    m_handle = -1;
315    m_mmaped_ptr = NULL;
316    m_phys_addr = 0;
317    m_host_addr = 0;
318    m_offset = 0;
319    m_size = 0;
320}
321
322int GoldfishAddressSpaceBlock::memoryMap(void *addr,
323                                         size_t len,
324                                         address_space_handle_t fd,
325                                         uint64_t off,
326                                         void** dst) {
327    void* ptr = ::mmap64(addr, len, PROT_WRITE, MAP_SHARED, fd, off);
328    if (MAP_FAILED == ptr) {
329        return errno;
330    } else {
331        *dst = ptr;
332        return 0;
333    }
334}
335
336void GoldfishAddressSpaceBlock::memoryUnmap(void *ptr, size_t size)
337{
338    ::munmap(ptr, size);
339}
340
341GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots)
342  : m_provider(useSharedSlots
343        ? GoldfishAddressSpaceSubdeviceType::SharedSlotsHostMemoryAllocator
344        : GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator),
345    m_useSharedSlots(useSharedSlots)
346{}
347
348bool GoldfishAddressSpaceHostMemoryAllocator::is_opened() const { return m_provider.is_opened(); }
349
350long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size)
351{
352    if (size == 0) {
353        return -EINVAL;
354    }
355    if (block->size() > 0) {
356        return -EINVAL;
357    }
358    if (!m_provider.is_opened()) {
359        return -ENODEV;
360    }
361
362    struct goldfish_address_space_ping request;
363    if (m_useSharedSlots) {
364        // shared memory slots are supported
365        ::memset(&request, 0, sizeof(request));
366        request.version = sizeof(request);
367        request.size = size;
368        request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
369
370        long ret = ioctl_ping(m_provider.m_handle, &request);
371        if (ret) {
372            return ret;
373        }
374        ret = static_cast<long>(request.metadata);
375        if (ret) {
376            return ret;
377        }
378
379        block->claimShared(&m_provider, request.offset, request.size);
380    } else {
381        // shared memory slots are not supported
382        if (!block->allocate(&m_provider, size)) {
383            return -ENOMEM;
384        }
385
386        ::memset(&request, 0, sizeof(request));
387        request.version = sizeof(request);
388        request.offset = block->offset();
389        request.size = block->size();
390        request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
391
392        long ret = ioctl_ping(m_provider.m_handle, &request);
393        if (ret) {
394            return ret;
395        }
396        ret = static_cast<long>(request.metadata);
397        if (ret) {
398            return ret;
399        }
400    }
401
402    block->mmap(0);
403    return 0;
404}
405
406void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block)
407{
408    if (block->size() == 0) {
409        return;
410    }
411
412    if (!m_provider.is_opened()) {
413        ALOGE("%s: device is not available", __func__);
414        ::abort();
415    }
416
417    if (block->guestPtr()) {
418        struct goldfish_address_space_ping request;
419        ::memset(&request, 0, sizeof(request));
420        request.version = sizeof(request);
421        request.offset = block->offset();
422        request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID;
423
424        const long ret = ioctl_ping(m_provider.m_handle, &request);
425        if (ret) {
426            ALOGE("%s: ioctl_ping failed, ret=%ld", __func__, ret);
427            ::abort();
428        }
429    }
430
431    block->replace(NULL);
432}
433
434address_space_handle_t goldfish_address_space_open() {
435    return ::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR);
436}
437
438void goldfish_address_space_close(address_space_handle_t handle) {
439    ::close(handle);
440}
441
442bool goldfish_address_space_allocate(
443    address_space_handle_t handle,
444    size_t size, uint64_t* phys_addr, uint64_t* offset) {
445
446    struct goldfish_address_space_allocate_block request;
447    ::memset(&request, 0, sizeof(request));
448    request.size = size;
449
450    long res = ioctl_allocate(handle, &request);
451
452    if (res) return false;
453
454    *phys_addr = request.phys_addr;
455    *offset = request.offset;
456    return true;
457}
458
459bool goldfish_address_space_free(
460    address_space_handle_t handle, uint64_t offset) {
461
462    long res = ioctl_deallocate(handle, offset);
463
464    if (res) {
465        ALOGE("ioctl_deallocate failed, res=%ld", res);
466        ::abort();
467    }
468
469    return true;
470}
471
472bool goldfish_address_space_claim_shared(
473    address_space_handle_t handle, uint64_t offset, uint64_t size) {
474
475    struct goldfish_address_space_claim_shared request;
476    request.offset = offset;
477    request.size = size;
478    long res = ioctl_claim_shared(handle, &request);
479
480    if (res) return false;
481
482    return true;
483}
484
485bool goldfish_address_space_unclaim_shared(
486        address_space_handle_t handle, uint64_t offset) {
487    long res = ioctl_unclaim_shared(handle, offset);
488    if (res) {
489        ALOGE("ioctl_unclaim_shared failed, res=%ld", res);
490        ::abort();
491    }
492
493    return true;
494}
495
496// pgoff is the offset into the page to return in the result
497void* goldfish_address_space_map(
498    address_space_handle_t handle,
499    uint64_t offset, uint64_t size,
500    uint64_t pgoff) {
501
502    void* res = ::mmap64(0, size, PROT_WRITE, MAP_SHARED, handle, offset);
503
504    if (res == MAP_FAILED) {
505        ALOGE("%s: failed to map. errno: %d\n", __func__, errno);
506        return 0;
507    }
508
509    return (void*)(((char*)res) + (uintptr_t)(pgoff & (PAGE_SIZE - 1)));
510}
511
512void goldfish_address_space_unmap(void* ptr, uint64_t size) {
513    void* pagePtr = (void*)(((uintptr_t)ptr) & ~(PAGE_SIZE - 1));
514    ::munmap(pagePtr, size);
515}
516
517bool goldfish_address_space_set_subdevice_type(
518    address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type,
519    address_space_handle_t* handle_out) {
520    struct goldfish_address_space_ping request;
521    request.metadata = (uint64_t)type;
522    *handle_out = handle;
523    return goldfish_address_space_ping(handle, &request);
524}
525
526bool goldfish_address_space_ping(
527    address_space_handle_t handle,
528    struct goldfish_address_space_ping* ping) {
529    long res = ioctl_ping(handle, ping);
530
531    if (res) {
532        ALOGE("%s: ping failed: errno: %d\n", __func__, errno);
533        return false;
534    }
535
536    return true;
537}
538