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