// 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 #define GET_STATUS_SAFE(result, member) \ ((result).ok() ? ((result).Unwrap()->member) : ZX_OK) using android::base::guest::AutoLock; using android::base::guest::Lock; using llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriver; using llcpp::fuchsia::hardware::goldfish::AddressSpaceDevice; using llcpp::fuchsia::hardware::goldfish::AddressSpaceChildDriverType; using llcpp::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 = std::make_unique(std::move(channel)); zx::channel child_driver_server, child_driver_client; zx_status_t status = zx::channel::create(0, &child_driver_server, &child_driver_client); if (status != ZX_OK) { ALOGE("%s: zx_channel_create failed: %d", __FUNCTION__, status); return; } auto result = m_device->OpenChildDriver( static_cast(0 /* graphics */), std::move(child_driver_server)); if (!result.ok()) { ALOGE("%s: failed to open child driver: %d", __FUNCTION__, result.status()); return; } m_child_driver = std::make_unique(std::move(child_driver_client)); } GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider() { } bool GoldfishAddressSpaceBlockProvider::is_opened() const { return !!m_device; } // 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; } AddressSpaceChildDriver::SyncClient* driver = provider->m_child_driver.get(); auto result = driver->AllocateBlock(size); if (!result.ok() || result.Unwrap()->res != ZX_OK) { ALOGE("%s: allocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res)); return false; } m_phys_addr = result.Unwrap()->paddr; m_vmo = result.Unwrap()->vmo.release(); m_size = size; 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; } 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; // auto result = m_driver->UnclaimShared(m_offset); // if (!result.ok() || result.Unwrap()->res != ZX_OK) { // ALOGE("%s: unclaim shared block failed: %d:%d", __func__, // result.status(), GET_STATUS_SAFE(result, res)); // } } else { auto result = m_driver->DeallocateBlock(m_phys_addr); if (!result.ok() || result.Unwrap()->res != ZX_OK) { ALOGE("%s: deallocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, 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; } AddressSpaceDevice::SyncClient* deviceSync = new AddressSpaceDevice::SyncClient(std::move(channel)); return (address_space_handle_t)deviceSync; } void goldfish_address_space_close(address_space_handle_t handle) { AddressSpaceDevice::SyncClient* deviceSync = reinterpret_cast< AddressSpaceDevice::SyncClient*>(handle); delete deviceSync; } bool goldfish_address_space_set_subdevice_type( address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type, address_space_handle_t* handle_out) { AddressSpaceDevice::SyncClient* deviceSync = reinterpret_cast< AddressSpaceDevice::SyncClient*>(handle); zx::channel child_driver_server, child_driver_client; zx_status_t status = zx::channel::create(0, &child_driver_server, &child_driver_client); if (status != ZX_OK) { ALOGE("%s: zx_channel_create failed: %d", __FUNCTION__, status); return false; } deviceSync->OpenChildDriver( static_cast(type), std::move(child_driver_server)); AddressSpaceChildDriver::SyncClient* childSync = new AddressSpaceChildDriver::SyncClient(std::move(child_driver_client)); // 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) { AddressSpaceChildDriver::SyncClient* deviceSync = reinterpret_cast< AddressSpaceChildDriver::SyncClient*>(handle); zx::vmo vmo; auto result = deviceSync->AllocateBlock(size); if (!result.ok() || result.Unwrap()->res != ZX_OK) { ALOGE("%s: allocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res)); return false; } *phys_addr = result.Unwrap()->paddr; vmo = std::move(result.Unwrap()->vmo); *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); AddressSpaceChildDriver::SyncClient* deviceSync = reinterpret_cast< AddressSpaceChildDriver::SyncClient*>(handle); auto result = deviceSync->DeallocateBlock(info.phys_addr); if (!result.ok() || result.Unwrap()->res != ZX_OK) { ALOGE("%s: deallocate block failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res)); return false; } return true; } bool goldfish_address_space_claim_shared( address_space_handle_t handle, uint64_t offset, uint64_t size) { AddressSpaceChildDriver::SyncClient* deviceSync = reinterpret_cast< AddressSpaceChildDriver::SyncClient*>(handle); zx::vmo vmo; auto result = deviceSync->ClaimSharedBlock(offset, size); if (!result.ok() || result.Unwrap()->res != ZX_OK) { ALOGE("%s: claim shared failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res)); return false; } vmo = std::move(result.Unwrap()->vmo); VmoStore::Info info = { vmo.release(), }; getVmoStore()->add(offset, info); return true; } bool goldfish_address_space_unclaim_shared( address_space_handle_t handle, uint64_t offset) { AddressSpaceChildDriver::SyncClient* deviceSync = reinterpret_cast< AddressSpaceChildDriver::SyncClient*>(handle); auto result = deviceSync->UnclaimSharedBlock(offset); if (!result.ok() || result.Unwrap()->res != ZX_OK) { ALOGE("%s: unclaim shared failed: %d:%d", __func__, result.status(), GET_STATUS_SAFE(result, res)); 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 address_space_ping* ping) { AddressSpaceChildDriverPingMessage fuchsiaPing = *(AddressSpaceChildDriverPingMessage*)ping; AddressSpaceChildDriver::SyncClient* deviceSync = reinterpret_cast< AddressSpaceChildDriver::SyncClient*>(handle); AddressSpaceChildDriverPingMessage res; auto result = deviceSync->Ping(fuchsiaPing); if (!result.ok() || result.Unwrap()->res != ZX_OK) { return false; } res = std::move(result.Unwrap()->ping); *ping = *(struct address_space_ping*)(&res); return true; }