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 "android/emulation/hostdevices/HostAddressSpace.h"
17
18#include <memory>
19
20#if PLATFORM_SDK_VERSION < 26
21#include <cutils/log.h>
22#else
23#include <log/log.h>
24#endif
25
26#include <errno.h>
27#include "goldfish_address_space.h"
28
29namespace {
30
31const int HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID = 1;
32const int HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID = 2;
33
34}  // namsepace
35
36using android::HostAddressSpaceDevice;
37using android::emulation::AddressSpaceDevicePingInfo;
38
39GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice)
40    : m_handle(HostAddressSpaceDevice::get()->open())
41{
42    if ((subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) && is_opened()) {
43        AddressSpaceDevicePingInfo request;
44        ::memset(&request, 0, sizeof(request));
45        request.metadata = subdevice;
46
47        HostAddressSpaceDevice::get()->ping(m_handle, &request);
48    }
49}
50
51GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
52{
53    if (is_opened()) {
54        HostAddressSpaceDevice::get()->close(m_handle);
55    }
56}
57
58bool GoldfishAddressSpaceBlockProvider::is_opened() const
59{
60    return m_handle > 0;
61}
62
63void GoldfishAddressSpaceBlockProvider::close()
64{
65    if (is_opened()) {
66        HostAddressSpaceDevice::get()->close(m_handle);
67        m_handle = 0;
68    }
69}
70
71address_space_handle_t GoldfishAddressSpaceBlockProvider::release()
72{
73    address_space_handle_t handle = m_handle;
74    m_handle = 0;
75    return handle;
76}
77
78void GoldfishAddressSpaceBlockProvider::closeHandle(address_space_handle_t handle)
79{
80    HostAddressSpaceDevice::get()->close(handle);
81}
82
83GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
84    : m_handle(0)
85    , m_mmaped_ptr(NULL)
86    , m_phys_addr(0)
87    , m_host_addr(0)
88    , m_offset(0)
89    , m_size(0)
90    , m_is_shared_mapping(false) {}
91
92GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
93{
94    destroy();
95}
96
97GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
98{
99    m_mmaped_ptr = rhs.m_mmaped_ptr;
100    m_phys_addr = rhs.m_phys_addr;
101    m_host_addr = rhs.m_host_addr;
102    m_offset = rhs.m_offset;
103    m_size = rhs.m_size;
104    m_is_shared_mapping = rhs.m_is_shared_mapping;
105    m_handle = rhs.m_handle;
106    return *this;
107}
108
109bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
110{
111    ALOGD("%s: Ask for block of size 0x%llx\n", __func__,
112         (unsigned long long)size);
113
114    destroy();
115
116    if (!provider->is_opened()) {
117        return false;
118    }
119
120    m_size = size;
121    m_offset =
122        HostAddressSpaceDevice::get()->allocBlock(
123            provider->m_handle, size, &m_phys_addr);
124    m_handle = provider->m_handle;
125    m_is_shared_mapping = false;
126
127    return true;
128}
129
130bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size)
131{
132    ALOGD("%s: Ask to claim region [0x%llx 0x%llx]\n", __func__,
133         (unsigned long long)offset,
134         (unsigned long long)offset + size);
135
136    destroy();
137
138    if (!provider->is_opened()) {
139        return false;
140    }
141
142    int claimRes = HostAddressSpaceDevice::get()->claimShared(
143            provider->m_handle, offset, size);
144
145    if (claimRes) {
146        ALOGE("%s: failed to claim shared region. Error: %d\n", __func__, claimRes);
147        return false;
148    }
149
150    m_size = size;
151    m_offset = offset;
152    m_handle = provider->m_handle;
153    m_is_shared_mapping = true;
154    m_phys_addr = HostAddressSpaceDevice::get()->offsetToPhysAddr(m_offset);
155
156    return true;
157}
158
159uint64_t GoldfishAddressSpaceBlock::physAddr() const
160{
161    return m_phys_addr;
162}
163
164uint64_t GoldfishAddressSpaceBlock::hostAddr() const
165{
166    return m_host_addr;
167}
168
169// In the host implementation:
170// mmap: is done by interpreting |host_addr| as the actual host address.
171void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
172{
173    if (m_size == 0) {
174        ALOGE("%s: called with zero size\n", __func__);
175        return NULL;
176    }
177
178    if (m_mmaped_ptr != nullptr) {
179        ALOGE("'mmap' called for an already mmaped address block 0x%llx %d", (unsigned long long)m_mmaped_ptr, nullptr == m_mmaped_ptr);
180        ::abort();
181    }
182
183    m_mmaped_ptr = (void*)(uintptr_t)(host_addr & (~(PAGE_SIZE - 1)));
184    m_host_addr = host_addr;
185
186    return guestPtr();
187}
188
189void *GoldfishAddressSpaceBlock::guestPtr() const
190{
191    return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
192}
193
194void GoldfishAddressSpaceBlock::destroy()
195{
196    if (m_mmaped_ptr && m_size) {
197        m_mmaped_ptr = NULL;
198    }
199
200    if (m_size) {
201        if (m_is_shared_mapping) {
202            HostAddressSpaceDevice::get()->unclaimShared(m_handle, m_offset);
203        } else {
204            HostAddressSpaceDevice::get()->freeBlock(m_handle, m_offset);
205        }
206        m_phys_addr = 0;
207        m_host_addr = 0;
208        m_offset = 0;
209        m_size = 0;
210    }
211}
212
213void GoldfishAddressSpaceBlock::release()
214{
215    m_handle = 0;
216    m_mmaped_ptr = NULL;
217    m_phys_addr = 0;
218    m_host_addr = 0;
219    m_offset = 0;
220    m_size = 0;
221}
222
223int GoldfishAddressSpaceBlock::memoryMap(void *addr,
224                                         size_t,
225                                         address_space_handle_t,
226                                         uint64_t,
227                                         void** dst) {
228    *dst = addr;
229    return 0;
230}
231
232void GoldfishAddressSpaceBlock::memoryUnmap(void *ptr, size_t size) {}
233
234GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots)
235  : m_provider(useSharedSlots
236        ? GoldfishAddressSpaceSubdeviceType::SharedSlotsHostMemoryAllocator
237        : GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator),
238    m_useSharedSlots(useSharedSlots)
239{}
240
241bool GoldfishAddressSpaceHostMemoryAllocator::is_opened() const { return m_provider.is_opened(); }
242
243long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size)
244{
245    if (size == 0) {
246        return -EINVAL;
247    }
248    if (block->size() > 0) {
249        return -EINVAL;
250    }
251    if (!m_provider.is_opened()) {
252        return -ENODEV;
253    }
254
255    AddressSpaceDevicePingInfo request;
256    if (m_useSharedSlots) {
257        ::memset(&request, 0, sizeof(request));
258        request.size = block->size();
259        request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
260
261        HostAddressSpaceDevice::get()->ping(m_provider.m_handle, &request);
262
263        block->claimShared(&m_provider, request.phys_addr, request.size);
264
265        void *hostPtr = HostAddressSpaceDevice::get()->getHostAddr(block->physAddr());
266        block->mmap(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(hostPtr)));
267    } else {
268        if (!block->allocate(&m_provider, size)) {
269            return -ENOMEM;
270        }
271
272        ::memset(&request, 0, sizeof(request));
273        request.phys_addr = block->physAddr();
274        request.size = block->size();
275        request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
276
277        HostAddressSpaceDevice::get()->ping(m_provider.m_handle, &request);
278
279        void *hostPtr = HostAddressSpaceDevice::get()->getHostAddr(block->physAddr());
280        block->mmap(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(hostPtr)));
281    }
282
283    return 0;
284}
285
286void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block)
287{
288    if (block->size() == 0) {
289        return;
290    }
291
292    if (!m_provider.is_opened()) {
293        ALOGE("%s: device is not available", __func__);
294        ::abort();
295    }
296
297    if (block->guestPtr()) {
298        AddressSpaceDevicePingInfo request;
299        ::memset(&request, 0, sizeof(request));
300        request.phys_addr = block->physAddr();
301        request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID;
302
303        HostAddressSpaceDevice::get()->ping(m_provider.m_handle, &request);
304    }
305
306    block->replace(NULL);
307}
308
309address_space_handle_t goldfish_address_space_open() {
310    return HostAddressSpaceDevice::get()->open();
311}
312
313void goldfish_address_space_close(address_space_handle_t handle) {
314    HostAddressSpaceDevice::get()->close(handle);
315}
316
317bool goldfish_address_space_allocate(
318    address_space_handle_t handle,
319    size_t size, uint64_t* phys_addr, uint64_t* offset) {
320
321    *offset =
322        HostAddressSpaceDevice::get()->allocBlock(
323            handle, size, phys_addr);
324
325    return true;
326}
327
328bool goldfish_address_space_free(
329    address_space_handle_t handle, uint64_t offset) {
330    HostAddressSpaceDevice::get()->freeBlock(handle, offset);
331    return true;
332}
333
334bool goldfish_address_space_claim_shared(
335    address_space_handle_t handle, uint64_t offset, uint64_t size) {
336
337    int claimRes = HostAddressSpaceDevice::get()->claimShared(
338        handle, offset, size);
339
340    if (claimRes) {
341        ALOGE("%s: failed to claim shared region. Error: %d\n", __func__, claimRes);
342        return false;
343    }
344
345    return true;
346}
347
348bool goldfish_address_space_unclaim_shared(
349        address_space_handle_t handle, uint64_t offset) {
350    HostAddressSpaceDevice::get()->unclaimShared(handle, offset);
351    return true;
352}
353
354// pgoff is the offset into the page to return in the result
355void* goldfish_address_space_map(
356    address_space_handle_t handle,
357    uint64_t offset, uint64_t size,
358    uint64_t pgoff) {
359
360    (void)size;
361
362    void* res = HostAddressSpaceDevice::get()->
363        getHostAddr(
364            HostAddressSpaceDevice::get()->offsetToPhysAddr(offset));
365
366    if (!res) {
367        ALOGE("%s: failed to map. errno: %d\n", __func__, errno);
368        return nullptr;
369    }
370
371    return (void*)(((char*)res) + (uintptr_t)(pgoff & (PAGE_SIZE - 1)));
372}
373
374// same address space
375void goldfish_address_space_unmap(void*, uint64_t) { }
376
377bool goldfish_address_space_set_subdevice_type(
378    address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type,
379    address_space_handle_t* handle_out) {
380    struct address_space_ping request;
381    request.metadata = (uint64_t)type;
382    *handle_out = handle;
383    return goldfish_address_space_ping(handle, &request);
384}
385
386bool goldfish_address_space_ping(
387    address_space_handle_t handle,
388    struct address_space_ping* ping) {
389
390    AddressSpaceDevicePingInfo* asHostPingInfo =
391        reinterpret_cast<AddressSpaceDevicePingInfo*>(ping);
392
393    HostAddressSpaceDevice::get()->ping(handle, asHostPingInfo);
394
395    return true;
396}
397