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 <memory>
17#include <fcntl.h>
18#include <lib/zx/channel.h>
19#include <lib/zx/vmo.h>
20#include <log/log.h>
21#include <stdlib.h>
22#include <sys/stat.h>
23#include <sys/types.h>
24#include <unistd.h>
25#include <zircon/process.h>
26#include <zircon/syscalls.h>
27#include <zircon/syscalls/object.h>
28
29#include "goldfish_address_space.h"
30#include "aemu/base/synchronization/AndroidLock.h"
31#include "services/service_connector.h"
32
33#include <unordered_map>
34
35#define GET_STATUS_SAFE(result, member) \
36    ((result).ok() ? ((result)->member) : ZX_OK)
37
38using gfxstream::guest::AutoLock;
39using gfxstream::guest::Lock;
40
41using fuchsia_hardware_goldfish::AddressSpaceChildDriver;
42using fuchsia_hardware_goldfish::AddressSpaceDevice;
43using fuchsia_hardware_goldfish::wire::AddressSpaceChildDriverType;
44using fuchsia_hardware_goldfish::wire::AddressSpaceChildDriverPingMessage;
45
46GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice) {
47
48    if (subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) {
49        ALOGE("%s: Tried to use a nontrivial subdevice when support has not been added\n", __func__);
50        abort();
51    }
52
53    fidl::ClientEnd<AddressSpaceDevice> channel{
54        zx::channel(GetConnectToServiceFunction()(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME))};
55    if (!channel) {
56        ALOGE("%s: failed to get service handle for " GOLDFISH_ADDRESS_SPACE_DEVICE_NAME,
57              __FUNCTION__);
58        return;
59    }
60    m_device = fidl::WireSyncClient<AddressSpaceDevice>(std::move(channel));
61
62    auto child_driver_ends =
63        fidl::CreateEndpoints<::fuchsia_hardware_goldfish::AddressSpaceChildDriver>();
64    if (!child_driver_ends.is_ok()) {
65        ALOGE("%s: zx_channel_create failed: %d", __FUNCTION__, child_driver_ends.status_value());
66        return;
67    }
68
69    auto result = m_device->OpenChildDriver(
70        static_cast<AddressSpaceChildDriverType>(0 /* graphics */),
71        std::move(child_driver_ends->server));
72    if (!result.ok()) {
73        ALOGE("%s: failed to open child driver: %d",
74              __FUNCTION__, result.status());
75        return;
76    }
77    m_child_driver = fidl::WireSyncClient<AddressSpaceChildDriver>(
78        std::move(child_driver_ends->client));
79}
80
81GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
82{
83}
84
85bool GoldfishAddressSpaceBlockProvider::is_opened() const
86{
87    return !!m_device;
88}
89
90// void GoldfishAddressSpaceBlockProvider::close() - not implemented
91// address_space_handle_t GoldfishAddressSpaceBlockProvider::release() - not imeplemented
92
93GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
94    : m_driver(NULL)
95    , m_vmo(ZX_HANDLE_INVALID)
96    , m_mmaped_ptr(NULL)
97    , m_phys_addr(0)
98    , m_host_addr(0)
99    , m_offset(0)
100    , m_size(0) {}
101
102GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
103{
104    destroy();
105}
106
107GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
108{
109    m_vmo = rhs.m_vmo;
110    m_mmaped_ptr = rhs.m_mmaped_ptr;
111    m_phys_addr = rhs.m_phys_addr;
112    m_host_addr = rhs.m_host_addr;
113    m_offset = rhs.m_offset;
114    m_size = rhs.m_size;
115    m_driver = rhs.m_driver;
116
117    return *this;
118}
119
120bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
121{
122    destroy();
123
124    if (!provider->is_opened()) {
125        return false;
126    }
127
128    const fidl::WireSyncClient<AddressSpaceChildDriver>& driver = provider->m_child_driver;
129
130    auto result = driver->AllocateBlock(size);
131    if (!result.ok() || result->res != ZX_OK) {
132        ALOGE("%s: allocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res));
133        return false;
134    }
135    m_phys_addr = result->paddr;
136    // TODO(fxbug.dev/42075554): Instead of storing raw handles we should
137    // consider using RAII zx::object instead.
138    m_vmo = result->vmo.release();
139
140    m_size = size;
141    m_offset = 0;
142    m_is_shared_mapping = false;
143    m_driver = &provider->m_child_driver;
144
145    return true;
146}
147
148bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size)
149{
150    ALOGE("%s: FATAL: not supported\n", __func__);
151    abort();
152}
153
154uint64_t GoldfishAddressSpaceBlock::physAddr() const
155{
156    return m_phys_addr;
157}
158
159uint64_t GoldfishAddressSpaceBlock::hostAddr() const
160{
161    return m_host_addr;
162}
163
164void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
165{
166    if (m_size == 0) {
167        ALOGE("%s: called with zero size\n", __func__);
168        return NULL;
169    }
170    if (m_mmaped_ptr) {
171        ALOGE("'mmap' called for an already mmaped address block");
172        ::abort();
173    }
174
175    bool nonzeroOffsetInPage = host_addr & (PAGE_SIZE - 1);
176    uint64_t extraBytes = nonzeroOffsetInPage ? PAGE_SIZE : 0;
177    m_size += extraBytes;
178
179    zx_vaddr_t ptr = 0;
180    zx_status_t status = zx_vmar_map(zx_vmar_root_self(),
181                                     ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
182                                     0, m_vmo,
183                                     m_offset,
184                                     m_size,
185                                     &ptr);
186    if (status != ZX_OK) {
187        ALOGE("%s: host memory map failed with size 0x%llx "
188              "off 0x%llx status %d\n",
189              __func__,
190              (unsigned long long)m_size,
191              (unsigned long long)m_offset, status);
192        return NULL;
193    }
194
195    m_mmaped_ptr = (void*)ptr;
196    m_host_addr = host_addr;
197    return guestPtr();
198}
199
200void *GoldfishAddressSpaceBlock::guestPtr() const
201{
202    return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1));
203}
204
205void GoldfishAddressSpaceBlock::destroy()
206{
207    if (m_mmaped_ptr && m_size) {
208        zx_vmar_unmap(zx_vmar_root_self(),
209                      (zx_vaddr_t)m_mmaped_ptr,
210                      m_size);
211        m_mmaped_ptr = NULL;
212    }
213
214    if (m_size) {
215        zx_handle_close(m_vmo);
216        m_vmo = ZX_HANDLE_INVALID;
217        if (m_is_shared_mapping) {
218            // TODO
219            ALOGE("%s: unsupported: GoldfishAddressSpaceBlock destroy() for shared regions\n", __func__);
220            abort();
221            // int32_t res = ZX_OK;
222            // auto result = m_driver->UnclaimShared(m_offset);
223            // if (!result.ok() || result->res != ZX_OK) {
224            //     ALOGE("%s: unclaim shared block failed: %d:%d", __func__,
225            //           result.status(), GET_STATUS_SAFE(result, res));
226            // }
227        } else {
228            auto result = (*m_driver)->DeallocateBlock(m_phys_addr);
229            if (!result.ok() || result->res != ZX_OK) {
230                ALOGE("%s: deallocate block failed: %d:%d", __func__,
231                      result.status(), GET_STATUS_SAFE(result, res));
232            }
233        }
234        m_driver = NULL;
235        m_phys_addr = 0;
236        m_host_addr = 0;
237        m_offset = 0;
238        m_size = 0;
239    }
240}
241
242GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots)
243  : m_provider(GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator) { }
244
245long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size)
246{
247    return 0;
248}
249
250void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block)
251{
252}
253
254class VmoStore {
255public:
256    struct Info {
257        zx_handle_t vmo = ZX_HANDLE_INVALID;
258        uint64_t phys_addr = 0;
259    };
260
261    void add(uint64_t offset, const Info& info) {
262        AutoLock lock(mLock);
263        mInfo[offset] = info;
264    }
265
266    void remove(uint64_t offset) {
267        AutoLock lock(mLock);
268        mInfo.erase(offset);
269    }
270
271    Info get(uint64_t offset) {
272        Info res;
273        AutoLock lock(mLock);
274        auto it = mInfo.find(offset);
275        if (it == mInfo.end()) {
276            ALOGE("VmoStore::%s cannot find info on offset 0x%llx\n", __func__,
277                  (unsigned long long)offset);
278            return res;
279        }
280        res = it->second;
281        return res;
282    }
283
284private:
285    Lock mLock;
286    std::unordered_map<uint64_t, Info> mInfo;
287};
288
289static Lock sVmoStoreInitLock;
290static VmoStore* sVmoStore = nullptr;
291
292static VmoStore* getVmoStore() {
293    AutoLock lock(sVmoStoreInitLock);
294    if (!sVmoStore) sVmoStore = new VmoStore;
295    return sVmoStore;
296}
297
298address_space_handle_t goldfish_address_space_open() {
299    fidl::ClientEnd<AddressSpaceDevice> channel{
300        zx::channel(GetConnectToServiceFunction()(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME))};
301    if (!channel) {
302        ALOGE("%s: failed to get service handle for " GOLDFISH_ADDRESS_SPACE_DEVICE_NAME,
303              __FUNCTION__);
304        return 0;
305    }
306    fidl::WireSyncClient<AddressSpaceDevice>*
307        deviceSync = new fidl::WireSyncClient<AddressSpaceDevice>(std::move(channel));
308    return (address_space_handle_t)deviceSync;
309}
310
311void goldfish_address_space_close(address_space_handle_t handle) {
312    fidl::WireSyncClient<AddressSpaceDevice>* deviceSync =
313        reinterpret_cast<
314            fidl::WireSyncClient<AddressSpaceDevice>*>(handle);
315    delete deviceSync;
316}
317
318bool goldfish_address_space_set_subdevice_type(
319    address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type,
320    address_space_handle_t* handle_out) {
321
322    auto* deviceSync =
323        reinterpret_cast<fidl::WireSyncClient<AddressSpaceDevice>*>(handle);
324
325    auto child_driver_ends =
326        fidl::CreateEndpoints<::fuchsia_hardware_goldfish::AddressSpaceChildDriver>();
327    if (!child_driver_ends.is_ok()) {
328        ALOGE("%s: zx_channel_create failed: %d", __FUNCTION__, child_driver_ends.status_value());
329        return false;
330    }
331
332    (*deviceSync)->OpenChildDriver(
333        static_cast<AddressSpaceChildDriverType>(type),
334        std::move(child_driver_ends->server));
335
336    fidl::WireSyncClient<AddressSpaceChildDriver>*
337        childSync = new fidl::WireSyncClient<AddressSpaceChildDriver>(std::move(child_driver_ends->client));
338
339    // On creating a subdevice, in our use cases we wont be needing the
340    // original device sync anymore, so get rid of it.
341    delete deviceSync;
342
343    *handle_out = (void*)childSync;
344
345    return true;
346}
347
348bool goldfish_address_space_allocate(
349    address_space_handle_t handle,
350    size_t size, uint64_t* phys_addr, uint64_t* offset) {
351    const auto& deviceSync =
352        *reinterpret_cast<
353            fidl::WireSyncClient<AddressSpaceChildDriver>*>(handle);
354
355    zx::vmo vmo;
356    auto result = deviceSync->AllocateBlock(size);
357    if (!result.ok() || result->res != ZX_OK) {
358        ALOGE("%s: allocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res));
359        return false;
360    }
361    *phys_addr = result->paddr;
362    vmo = std::move(result->vmo);
363
364    *offset = 0;
365
366    // TODO(fxbug.dev/42075554): Instead of storing raw handles we should
367    // consider using RAII zx::object instead.
368    VmoStore::Info info = {
369        vmo.release(),
370        *phys_addr,
371    };
372
373    getVmoStore()->add(*offset, info);
374    return true;
375}
376
377bool goldfish_address_space_free(
378    address_space_handle_t handle, uint64_t offset) {
379    auto info = getVmoStore()->get(offset);
380    if (info.vmo == ZX_HANDLE_INVALID) return false;
381    zx_handle_close(info.vmo);
382
383    const auto& deviceSync =
384        *reinterpret_cast<
385            fidl::WireSyncClient<AddressSpaceChildDriver>*>(handle);
386
387    auto result = deviceSync->DeallocateBlock(info.phys_addr);
388    if (!result.ok() || result->res != ZX_OK) {
389        ALOGE("%s: deallocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res));
390        return false;
391    }
392
393    return true;
394}
395
396bool goldfish_address_space_claim_shared(
397    address_space_handle_t handle, uint64_t offset, uint64_t size) {
398
399    const auto& deviceSync =
400        *reinterpret_cast<
401            fidl::WireSyncClient<AddressSpaceChildDriver>*>(handle);
402
403    zx::vmo vmo;
404    auto result = deviceSync->ClaimSharedBlock(offset, size);
405    if (!result.ok() || result->res != ZX_OK) {
406        ALOGE("%s: claim shared failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res));
407        return false;
408    }
409    vmo = std::move(result->vmo);
410
411    // TODO(fxbug.dev/42075554): Instead of storing raw handles we should
412    // consider using RAII zx::object instead.
413    VmoStore::Info info = {
414        vmo.release(),
415    };
416
417    getVmoStore()->add(offset, info);
418
419    return true;
420}
421
422bool goldfish_address_space_unclaim_shared(
423    address_space_handle_t handle, uint64_t offset) {
424    auto info = getVmoStore()->get(offset);
425    if (info.vmo == ZX_HANDLE_INVALID) {
426        return false;
427    }
428    zx_handle_close(info.vmo);
429
430    const auto& deviceSync =
431        *reinterpret_cast<
432            fidl::WireSyncClient<AddressSpaceChildDriver>*>(handle);
433
434    auto result = deviceSync->UnclaimSharedBlock(offset);
435    if (!result.ok() || result->res != ZX_OK) {
436        ALOGE("%s: unclaim shared failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res));
437        return false;
438    }
439
440    getVmoStore()->remove(offset);
441    return true;
442}
443
444// pgoff is the offset into the page to return in the result
445void* goldfish_address_space_map(
446    address_space_handle_t handle,
447    uint64_t offset, uint64_t size,
448    uint64_t pgoff) {
449
450    auto info = getVmoStore()->get(offset);
451    if (info.vmo == ZX_HANDLE_INVALID) return nullptr;
452
453    zx_vaddr_t ptr = 0;
454    zx_status_t status =
455        zx_vmar_map(zx_vmar_root_self(),
456                    ZX_VM_PERM_READ | ZX_VM_PERM_WRITE,
457                    0, info.vmo,
458                    0, size,
459                    &ptr);
460    return (void*)(((char*)ptr) + (uintptr_t)(pgoff & (PAGE_SIZE - 1)));
461}
462
463void goldfish_address_space_unmap(void* ptr, uint64_t size) {
464    zx_vmar_unmap(zx_vmar_root_self(),
465                  (zx_vaddr_t)(((uintptr_t)ptr) & (uintptr_t)(~(PAGE_SIZE - 1))),
466                  size);
467}
468
469bool goldfish_address_space_ping(
470    address_space_handle_t handle,
471    struct address_space_ping* ping) {
472
473    AddressSpaceChildDriverPingMessage fuchsiaPing =
474        *(AddressSpaceChildDriverPingMessage*)ping;
475
476    const auto& deviceSync =
477        *reinterpret_cast<
478            fidl::WireSyncClient<AddressSpaceChildDriver>*>(handle);
479
480    AddressSpaceChildDriverPingMessage res;
481    auto result = deviceSync->Ping(fuchsiaPing);
482    if (!result.ok() || result->res != ZX_OK) {
483        return false;
484    }
485    res = std::move(result->ping);
486
487    *ping = *(struct address_space_ping*)(&res);
488    return true;
489}
490