| // 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 <linux/types.h> |
| #include <linux/ioctl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/mman.h> |
| #include <sys/ioctl.h> |
| #include <fcntl.h> |
| #include <unistd.h> |
| #include <cstdlib> |
| #include <errno.h> |
| #include <memory> |
| |
| #if PLATFORM_SDK_VERSION < 26 |
| #include <cutils/log.h> |
| #else |
| #include <log/log.h> |
| #endif |
| |
| #include "goldfish_address_space.h" |
| |
| namespace { |
| |
| struct goldfish_address_space_allocate_block { |
| __u64 size; |
| __u64 offset; |
| __u64 phys_addr; |
| }; |
| |
| struct goldfish_address_space_ping { |
| __u64 offset; |
| __u64 size; |
| __u64 metadata; |
| __u64 wait_offset; |
| __u32 wait_flags; |
| __u32 direction; |
| }; |
| |
| #define GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC 'G' |
| #define GOLDFISH_ADDRESS_SPACE_IOCTL_OP(OP, T) _IOWR(GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC, OP, T) |
| #define GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(10, struct goldfish_address_space_allocate_block) |
| #define GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(11, __u64) |
| #define GOLDFISH_ADDRESS_SPACE_IOCTL_PING GOLDFISH_ADDRESS_SPACE_IOCTL_OP(12, struct goldfish_address_space_ping) |
| |
| const char GOLDFISH_ADDRESS_SPACE_DEVICE_NAME[] = "/dev/goldfish_address_space"; |
| |
| const int DEVICE_TYPE_HOST_MEMORY_ALLOCATOR_ID = 5; |
| const int HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID = 1; |
| const int HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID = 2; |
| |
| long ioctl_allocate(int fd, struct goldfish_address_space_allocate_block *request) |
| { |
| return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK, request); |
| } |
| |
| long ioctl_deallocate(int fd, uint64_t offset) |
| { |
| return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK, &offset); |
| } |
| |
| long ioctl_ping(int fd, struct goldfish_address_space_ping *request) |
| { |
| return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_PING, request); |
| } |
| |
| } // namespace |
| |
| GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider() |
| : m_fd(::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR)) {} |
| |
| GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider() |
| { |
| ::close(m_fd); |
| } |
| |
| bool GoldfishAddressSpaceBlockProvider::is_opened() const |
| { |
| return m_fd >= 0; |
| } |
| |
| void GoldfishAddressSpaceBlockProvider::close() |
| { |
| if (is_opened()) { |
| ::close(m_fd); |
| m_fd = -1; |
| } |
| } |
| |
| GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock() |
| : m_fd(-1) |
| , 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_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_fd = rhs.m_fd; |
| |
| 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; |
| } |
| |
| struct goldfish_address_space_allocate_block request; |
| ::memset(&request, 0, sizeof(request)); |
| request.size = size; |
| |
| long res = ioctl_allocate(provider->m_fd, &request); |
| if (res) { |
| return false; |
| } else { |
| m_phys_addr = request.phys_addr; |
| m_offset = request.offset; |
| m_size = request.size; |
| m_fd = provider->m_fd; |
| |
| ALOGD("%s: ioctl allocate returned offset 0x%llx size 0x%llx\n", __func__, |
| (unsigned long long)m_offset, |
| (unsigned long long)m_size); |
| |
| return true; |
| } |
| } |
| |
| 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(); |
| } |
| |
| void *result = ::mmap64(NULL, m_size, PROT_WRITE, MAP_SHARED, m_fd, m_offset); |
| if (result == MAP_FAILED) { |
| ALOGE("%s: host memory map failed with size 0x%llx " |
| "off 0x%llx errno %d\n", |
| __func__, |
| (unsigned long long)m_size, |
| (unsigned long long)m_offset, errno); |
| return NULL; |
| } else { |
| m_mmaped_ptr = result; |
| m_host_addr = host_addr; |
| return guestPtr(); |
| } |
| } |
| |
| void *GoldfishAddressSpaceBlock::guestPtr() const |
| { |
| return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (PAGE_SIZE - 1)); |
| } |
| |
| void GoldfishAddressSpaceBlock::destroy() |
| { |
| if (m_mmaped_ptr && m_size) { |
| ::munmap(m_mmaped_ptr, m_size); |
| m_mmaped_ptr = NULL; |
| } |
| |
| if (m_size) { |
| const long res = ioctl_deallocate(m_fd, m_offset); |
| if (res) { |
| ALOGE("ioctl_deallocate failed, res=%ld", res); |
| ::abort(); |
| } |
| |
| m_phys_addr = 0; |
| m_host_addr = 0; |
| m_offset = 0; |
| m_size = 0; |
| } |
| } |
| |
| GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator() |
| { |
| if (m_provider.is_opened()) { |
| struct goldfish_address_space_ping request; |
| ::memset(&request, 0, sizeof(request)); |
| request.metadata = DEVICE_TYPE_HOST_MEMORY_ALLOCATOR_ID; |
| |
| const long ret = ioctl_ping(m_provider.m_fd, &request); |
| if (ret) { |
| ALOGE("%s: ioctl_ping failed for device_type=%llu, ret=%ld", |
| __func__, DEVICE_TYPE_HOST_MEMORY_ALLOCATOR_ID, ret); |
| m_provider.close(); |
| } |
| } |
| } |
| |
| long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size) |
| { |
| if (size == 0) { |
| return -EINVAL; |
| } |
| if (block->size() > 0) { |
| return -EINVAL; |
| } |
| if (!m_provider.is_opened()) { |
| return -ENODEV; |
| } |
| if (!block->allocate(&m_provider, size)) { |
| return -ENOMEM; |
| } |
| |
| struct goldfish_address_space_ping request; |
| ::memset(&request, 0, sizeof(request)); |
| request.offset = block->offset(); |
| request.size = block->size(); |
| request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID; |
| |
| long ret = ioctl_ping(m_provider.m_fd, &request); |
| if (ret) { |
| return ret; |
| } |
| ret = static_cast<long>(request.metadata); |
| if (ret) { |
| return ret; |
| } |
| |
| block->mmap(0); |
| return 0; |
| } |
| |
| void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block) |
| { |
| if (block->size() == 0) { |
| return; |
| } |
| |
| if (!m_provider.is_opened()) { |
| ALOGE("%s: device is not available", __func__); |
| ::abort(); |
| } |
| |
| if (block->guestPtr()) { |
| struct goldfish_address_space_ping request; |
| ::memset(&request, 0, sizeof(request)); |
| request.offset = block->offset(); |
| request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID; |
| |
| const long ret = ioctl_ping(m_provider.m_fd, &request); |
| if (ret) { |
| ALOGE("%s: ioctl_ping failed, ret=%ld", __func__, ret); |
| ::abort(); |
| } |
| } |
| |
| block->replace(NULL); |
| } |