// Copyright (C) 2019 The Android Open Source Project // Copyright (C) 2019 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include #include #include #include #include #include #include #include #include #include #include #include #include "goldfish_address_space.h" #include "android/base/synchronization/AndroidLock.h" #include "services/service_connector.h" #include using android::base::guest::AutoLock; using android::base::guest::Lock; using fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr; using fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr; using fuchsia::hardware::goldfish::AddressSpaceChildDriverType; using fuchsia::hardware::goldfish::AddressSpaceChildDriverPingMessage; GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice) { if (subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) { ALOGE("%s: Tried to use a nontrivial subdevice when support has not been added\n", __func__); abort(); } zx::channel channel(GetConnectToServiceFunction()(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME)); if (!channel) { ALOGE("%s: failed to get service handle for " GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, __FUNCTION__); return; } m_device.Bind(std::move(channel)); zx_status_t status = (*m_device).OpenChildDriver( static_cast(0 /* graphics */), m_child_driver.NewRequest()); if (status != ZX_OK) { ALOGE("%s: failed to open child driver: %d", __FUNCTION__, status); return; } } GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider() { } bool GoldfishAddressSpaceBlockProvider::is_opened() const { return m_device.is_bound(); } // void GoldfishAddressSpaceBlockProvider::close() - not implemented // address_space_handle_t GoldfishAddressSpaceBlockProvider::release() - not imeplemented GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock() : m_driver(NULL) , m_vmo(ZX_HANDLE_INVALID) , m_mmaped_ptr(NULL) , m_phys_addr(0) , m_host_addr(0) , m_offset(0) , m_size(0) {} GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock() { destroy(); } GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs) { m_vmo = rhs.m_vmo; m_mmaped_ptr = rhs.m_mmaped_ptr; m_phys_addr = rhs.m_phys_addr; m_host_addr = rhs.m_host_addr; m_offset = rhs.m_offset; m_size = rhs.m_size; m_driver = rhs.m_driver; return *this; } bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size) { ALOGD("%s: Ask for block of size 0x%llx\n", __func__, (unsigned long long)size); destroy(); if (!provider->is_opened()) { return false; } fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr* driver = &provider->m_child_driver; int32_t res = ZX_OK; zx::vmo vmo; zx_status_t status = (*driver)->AllocateBlock(size, &res, &m_phys_addr, &vmo); if (status != ZX_OK || res != ZX_OK) { ALOGE("%s: allocate block failed: %d:%d", __func__, status, res); return false; } m_size = size; m_vmo = vmo.release(); m_offset = 0; m_is_shared_mapping = false; ALOGD("%s: allocate returned offset 0x%llx size 0x%llx\n", __func__, (unsigned long long)m_offset, (unsigned long long)m_size); m_driver = driver; return true; } bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size) { ALOGE("%s: FATAL: not supported\n", __func__); abort(); } uint64_t GoldfishAddressSpaceBlock::physAddr() const { return m_phys_addr; } uint64_t GoldfishAddressSpaceBlock::hostAddr() const { return m_host_addr; } void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr) { if (m_size == 0) { ALOGE("%s: called with zero size\n", __func__); return NULL; } if (m_mmaped_ptr) { ALOGE("'mmap' called for an already mmaped address block"); ::abort(); } zx_vaddr_t ptr = 0; zx_status_t status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, m_vmo, m_offset, m_size, &ptr); if (status != ZX_OK) { ALOGE("%s: host memory map failed with size 0x%llx " "off 0x%llx status %d\n", __func__, (unsigned long long)m_size, (unsigned long long)m_offset, status); return NULL; } else { m_mmaped_ptr = (void*)ptr; m_host_addr = host_addr; return guestPtr(); } } void *GoldfishAddressSpaceBlock::guestPtr() const { return reinterpret_cast(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1)); } void GoldfishAddressSpaceBlock::destroy() { if (m_mmaped_ptr && m_size) { zx_vmar_unmap(zx_vmar_root_self(), (zx_vaddr_t)m_mmaped_ptr, m_size); m_mmaped_ptr = NULL; } if (m_size) { zx_handle_close(m_vmo); m_vmo = ZX_HANDLE_INVALID; if (m_is_shared_mapping) { // TODO ALOGE("%s: unsupported: GoldfishAddressSpaceBlock destroy() for shared regions\n", __func__); abort(); // int32_t res = ZX_OK; // zx_status_t status = (*m_driver)->UnclaimShared(m_offset, &res); // if (status != ZX_OK || res != ZX_OK) { // ALOGE("%s: unclaim shared block failed: %d:%d", __func__, status, res); // } } else { int32_t res = ZX_OK; zx_status_t status = (*m_driver)->DeallocateBlock(m_phys_addr, &res); if (status != ZX_OK || res != ZX_OK) { ALOGE("%s: deallocate block failed: %d:%d", __func__, status, res); } } m_driver = NULL; m_phys_addr = 0; m_host_addr = 0; m_offset = 0; m_size = 0; } } GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots) : m_provider(GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator) { } long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size) { return 0; } void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block) { } class VmoStore { public: struct Info { zx_handle_t vmo = ZX_HANDLE_INVALID; uint64_t phys_addr = 0; }; void add(uint64_t offset, const Info& info) { AutoLock lock(mLock); mInfo[offset] = info; } void remove(uint64_t offset) { AutoLock lock(mLock); mInfo.erase(offset); } Info get(uint64_t offset) { Info res; AutoLock lock(mLock); auto it = mInfo.find(offset); if (it == mInfo.end()) { ALOGE("VmoStore::%s cannot find info on offset 0x%llx\n", __func__, (unsigned long long)offset); return res; } res = it->second; return res; } private: Lock mLock; std::unordered_map mInfo; }; static Lock sVmoStoreInitLock; static VmoStore* sVmoStore = nullptr; static VmoStore* getVmoStore() { AutoLock lock(sVmoStoreInitLock); if (!sVmoStore) sVmoStore = new VmoStore; return sVmoStore; } address_space_handle_t goldfish_address_space_open() { zx::channel channel(GetConnectToServiceFunction()(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME)); if (!channel) { ALOGE("%s: failed to get service handle for " GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, __FUNCTION__); return 0; } fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr* deviceSync = new fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr; deviceSync->Bind(std::move(channel)); return (address_space_handle_t)deviceSync; } void goldfish_address_space_close(address_space_handle_t handle) { fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr* deviceSync = reinterpret_cast< fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr*>(handle); delete deviceSync; } bool goldfish_address_space_set_subdevice_type( address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type, address_space_handle_t* handle_out) { fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr* deviceSync = reinterpret_cast< fuchsia::hardware::goldfish::AddressSpaceDeviceSyncPtr*>(handle); fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr* childSync = new fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr; zx_status_t res = (*(*deviceSync)).OpenChildDriver( static_cast(type), (*childSync).NewRequest()); // On creating a subdevice, in our use cases we wont be needing the // original device sync anymore, so get rid of it. delete deviceSync; *handle_out = (void*)childSync; return true; } bool goldfish_address_space_allocate( address_space_handle_t handle, size_t size, uint64_t* phys_addr, uint64_t* offset) { fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr* deviceSync = reinterpret_cast< fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr*>(handle); int32_t res = ZX_OK; zx::vmo vmo; zx_status_t status = (*(*deviceSync)).AllocateBlock(size, &res, phys_addr, &vmo); if (status != ZX_OK || res != ZX_OK) { ALOGE("%s: allocate block failed: %d:%d", __func__, status, res); return false; } *offset = 0; VmoStore::Info info = { vmo.release(), *phys_addr, }; getVmoStore()->add(*offset, info); return true; } bool goldfish_address_space_free( address_space_handle_t handle, uint64_t offset) { auto info = getVmoStore()->get(offset); if (info.vmo == ZX_HANDLE_INVALID) return false; zx_handle_close(info.vmo); fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr* deviceSync = reinterpret_cast< fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr*>(handle); int32_t res = ZX_OK; zx_status_t status = (*(*deviceSync)).DeallocateBlock(info.phys_addr, &res); if (status != ZX_OK || res != ZX_OK) { ALOGE("%s: deallocate block failed: %d:%d", __func__, status, res); return false; } return true; } bool goldfish_address_space_claim_shared( address_space_handle_t handle, uint64_t offset, uint64_t size) { fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr* deviceSync = reinterpret_cast< fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr*>(handle); zx::vmo vmo; zx_status_t res; zx_status_t status = (*(*deviceSync)).ClaimSharedBlock(offset, size, &res, &vmo); VmoStore::Info info = { vmo.release(), }; getVmoStore()->add(offset, info); if (status != ZX_OK) return false; return true; } bool goldfish_address_space_unclaim_shared( address_space_handle_t handle, uint64_t offset) { fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr* deviceSync = reinterpret_cast< fuchsia::hardware::goldfish::AddressSpaceChildDriverSyncPtr*>(handle); zx::vmo vmo; zx_status_t res; zx_status_t status = (*(*deviceSync)).UnclaimSharedBlock(offset, &res); if (status != ZX_OK) return false; getVmoStore()->remove(offset); return true; } // pgoff is the offset into the page to return in the result void* goldfish_address_space_map( address_space_handle_t handle, uint64_t offset, uint64_t size, uint64_t pgoff) { auto info = getVmoStore()->get(offset); if (info.vmo == ZX_HANDLE_INVALID) return nullptr; zx_vaddr_t ptr = 0; zx_status_t status = zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, info.vmo, 0, size, &ptr); return (void*)(((char*)ptr) + (uintptr_t)(pgoff & (PAGE_SIZE - 1))); } void goldfish_address_space_unmap(void* ptr, uint64_t size) { zx_vmar_unmap(zx_vmar_root_self(), (zx_vaddr_t)(((uintptr_t)ptr) & (uintptr_t)(~(PAGE_SIZE - 1))), size); } bool goldfish_address_space_ping( address_space_handle_t handle, struct goldfish_address_space_ping* ping) { AddressSpaceChildDriverPingMessage fuchsiaPing = *(AddressSpaceChildDriverPingMessage*)ping; AddressSpaceChildDriverSyncPtr* deviceSync = reinterpret_cast< AddressSpaceChildDriverSyncPtr*>(handle); AddressSpaceChildDriverPingMessage res; zx_status_t pingStatus; zx_status_t status = (*(*deviceSync)).Ping(fuchsiaPing, &pingStatus, &res); if (pingStatus != ZX_OK) { return false; } *ping = *(struct goldfish_address_space_ping*)(&res); return true; }