blob: 6726895af74695bf3b73623ff0b55a0742cb05dd [file] [log] [blame]
/*
* Copyright (C) 2016, 2018-2020 ARM Limited. All rights reserved.
*
* Copyright (C) 2008 The Android Open Source Project
*
* 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 "mali_gralloc_reference.h"
#include <android-base/thread_annotations.h>
#include <hardware/gralloc1.h>
#include <algorithm>
#include <map>
#include <mutex>
#include "allocator/mali_gralloc_ion.h"
#include "mali_gralloc_buffer.h"
class BufferManager {
private:
// This struct for now is just for validation and reference counting. When we
// are sure that private_handle_t::bases is not being used outside gralloc, this
// should become the only place where address mapping is maintained and can be
// queried from.
struct MappedData {
std::array<void *, MAX_BUFFER_FDS> bases;
size_t alloc_sizes[MAX_BUFFER_FDS] = {};
void *metadata_vaddr;
size_t metadata_size;
uint64_t ref_count = 0;
};
BufferManager() = default;
std::mutex lock;
std::map<const private_handle_t *, std::unique_ptr<MappedData>> buffer_map GUARDED_BY(lock);
static off_t get_buffer_size(unsigned int fd) {
off_t current = lseek(fd, 0, SEEK_CUR);
off_t size = lseek(fd, 0, SEEK_END);
lseek(fd, current, SEEK_SET);
return size;
}
// TODO(b/296934447): AION buffers set the size of the buffer themselves. That size exceeds the
// size of the actual allocated dmabuf.
static bool dmabuf_sanity_check(buffer_handle_t handle, bool skip_buffer_size_check = false) {
private_handle_t *hnd =
static_cast<private_handle_t *>(const_cast<native_handle_t *>(handle));
int valid_fd_count = std::find(hnd->fds, hnd->fds + MAX_FDS, -1) - hnd->fds;
// One fd is reserved for metadata which is not accounted for in fd_count
if (hnd->fd_count != valid_fd_count - 1) {
MALI_GRALLOC_LOGE("%s failed: count of valid buffer fds does not match fd_count",
__func__);
return false;
}
auto check_pid = [&](int fd, uint64_t allocated_size) -> bool {
auto size = get_buffer_size(fd);
auto size_padding = size - (off_t)allocated_size;
if ((size != -1) && ((size_padding < 0) || (size_padding > PAGE_SIZE))) {
MALI_GRALLOC_LOGE("%s failed: fd (%d) size (%jd) is not within a PAGE_SIZE of "
"expected size (%" PRIx64 ")",
__func__, fd, static_cast<intmax_t>(size), allocated_size);
return false;
}
return true;
};
// Check client facing dmabufs
if (!skip_buffer_size_check) {
for (auto i = 0; i < hnd->fd_count; i++) {
if (!check_pid(hnd->fds[i], hnd->alloc_sizes[i])) {
MALI_GRALLOC_LOGE("%s failed: Size check failed for alloc_sizes[%d]", __func__,
i);
return false;
}
}
}
// Check metadata dmabuf
if (!check_pid(hnd->get_share_attr_fd(), hnd->attr_size)) {
MALI_GRALLOC_LOGE("%s failed: Size check failed for metadata fd", __func__);
return false;
}
return true;
}
bool map_buffer_locked(buffer_handle_t handle) REQUIRES(lock) {
auto data_oe = get_validated_data_locked(handle);
if (!data_oe.has_value()) {
return false;
}
MappedData &data = data_oe.value();
// Return early if buffer is already mapped
if (data.bases[0] != nullptr) {
return true;
}
if (!dmabuf_sanity_check(handle)) {
return false;
}
private_handle_t *hnd =
reinterpret_cast<private_handle_t *>(const_cast<native_handle *>(handle));
data.bases = mali_gralloc_ion_map(hnd);
if (data.bases[0] == nullptr) {
return false;
}
for (auto i = 0; i < MAX_BUFFER_FDS; i++) {
data.alloc_sizes[i] = hnd->alloc_sizes[i];
}
return true;
}
bool map_metadata_locked(buffer_handle_t handle) REQUIRES(lock) {
auto data_oe = get_validated_data_locked(handle);
if (!data_oe.has_value()) {
return false;
}
MappedData &data = data_oe.value();
// Return early if buffer is already mapped
if (data.metadata_vaddr != nullptr) {
return true;
}
if (!dmabuf_sanity_check(handle, /*skip_buffer_size_check=*/true)) {
return false;
}
private_handle_t *hnd =
reinterpret_cast<private_handle_t *>(const_cast<native_handle *>(handle));
data.metadata_vaddr = mmap(nullptr, hnd->attr_size, PROT_READ | PROT_WRITE, MAP_SHARED,
hnd->get_share_attr_fd(), 0);
if (data.metadata_vaddr == nullptr) {
return false;
}
data.metadata_size = hnd->attr_size;
return true;
}
bool validate_locked(buffer_handle_t handle) REQUIRES(lock) {
if (private_handle_t::validate(handle) < 0) {
MALI_GRALLOC_LOGE("Reference invalid buffer %p, returning error", handle);
return false;
}
const auto *hnd = reinterpret_cast<private_handle_t *>(const_cast<native_handle *>(handle));
auto it = buffer_map.find(hnd);
if (it == buffer_map.end()) {
MALI_GRALLOC_LOGE("Reference unimported buffer %p, returning error", handle);
return false;
}
auto &data = *(it->second.get());
if (data.bases[0] != nullptr) {
for (auto i = 0; i < MAX_BUFFER_FDS; i++) {
if (data.alloc_sizes[i] != hnd->alloc_sizes[i]) {
MALI_GRALLOC_LOGE(
"Validation failed: Buffer attributes inconsistent with mapper");
return false;
}
}
} else {
for (auto i = 0; i < MAX_BUFFER_FDS; i++) {
if (data.bases[i] != nullptr) {
MALI_GRALLOC_LOGE("Validation failed: Expected nullptr for unmapped buffer");
return false;
}
}
}
return true;
}
std::optional<std::reference_wrapper<MappedData>> get_validated_data_locked(
buffer_handle_t handle) REQUIRES(lock) {
if (!validate_locked(handle)) {
return {};
}
private_handle_t *hnd =
reinterpret_cast<private_handle_t *>(const_cast<native_handle *>(handle));
auto it = buffer_map.find(hnd);
if (it == buffer_map.end()) {
MALI_GRALLOC_LOGE("Trying to release a non-imported buffer");
return {};
}
MappedData &data = *(it->second.get());
if (data.ref_count == 0) {
MALI_GRALLOC_LOGE("BUG: Found an imported buffer with ref count 0, expect errors");
}
return data;
}
public:
static BufferManager &getInstance() {
static BufferManager instance;
return instance;
}
int retain(buffer_handle_t handle) EXCLUDES(lock) {
if (private_handle_t::validate(handle) < 0) {
MALI_GRALLOC_LOGE("Registering/Retaining invalid buffer %p, returning error", handle);
return -EINVAL;
}
std::lock_guard<std::mutex> _l(lock);
private_handle_t *hnd =
reinterpret_cast<private_handle_t *>(const_cast<native_handle *>(handle));
auto it = buffer_map.find(hnd);
if (it == buffer_map.end()) {
bool success = false;
auto _data = std::make_unique<MappedData>();
std::tie(it, success) = buffer_map.insert({hnd, std::move(_data)});
if (!success) {
MALI_GRALLOC_LOGE("Failed to create buffer data mapping");
return -EINVAL;
}
} else if (it->second->ref_count == 0) {
MALI_GRALLOC_LOGE("BUG: Import counter of an imported buffer is 0, expect errors");
}
auto &data = *(it->second.get());
data.ref_count++;
return 0;
}
int map(buffer_handle_t handle) EXCLUDES(lock) {
std::lock_guard<std::mutex> _l(lock);
if (!map_buffer_locked(handle)) {
return -EINVAL;
}
return 0;
}
int release(buffer_handle_t handle) EXCLUDES(lock) {
std::lock_guard<std::mutex> _l(lock);
auto data_oe = get_validated_data_locked(handle);
if (!data_oe.has_value()) {
return -EINVAL;
}
MappedData &data = data_oe.value();
if (data.ref_count == 0) {
MALI_GRALLOC_LOGE("BUG: Reference held for buffer whose counter is 0");
return -EINVAL;
}
data.ref_count--;
if (data.ref_count == 0) {
private_handle_t *hnd =
reinterpret_cast<private_handle_t *>(const_cast<native_handle *>(handle));
auto it = buffer_map.find(hnd);
if (data.bases[0] != nullptr) {
mali_gralloc_ion_unmap(hnd, data.bases);
}
if (data.metadata_vaddr != nullptr) {
munmap(data.metadata_vaddr, data.metadata_size);
data.metadata_vaddr = nullptr;
}
buffer_map.erase(it);
}
return 0;
}
int validate(buffer_handle_t handle) EXCLUDES(lock) {
std::lock_guard<std::mutex> _l(lock);
if (!validate_locked(handle)) {
return -EINVAL;
}
return 0;
}
std::optional<void *> get_buf_addr(buffer_handle_t handle) {
std::lock_guard<std::mutex> _l(lock);
auto data_oe = get_validated_data_locked(handle);
if (!data_oe.has_value()) {
return {};
}
MappedData &data = data_oe.value();
if (data.bases[0] == nullptr) {
MALI_GRALLOC_LOGE("BUG: Called %s for an un-mapped buffer", __FUNCTION__);
return {};
}
return data.bases[0];
}
std::optional<void *> get_metadata_addr(buffer_handle_t handle) {
std::lock_guard<std::mutex> _l(lock);
auto data_oe = get_validated_data_locked(handle);
if (!data_oe.has_value()) {
return {};
}
MappedData &data = data_oe.value();
if (data.metadata_vaddr == nullptr) {
if (!map_metadata_locked(handle)) {
return {};
}
}
return data.metadata_vaddr;
}
};
int mali_gralloc_reference_retain(buffer_handle_t handle) {
return BufferManager::getInstance().retain(handle);
}
int mali_gralloc_reference_map(buffer_handle_t handle) {
return BufferManager::getInstance().map(handle);
}
int mali_gralloc_reference_release(buffer_handle_t handle) {
return BufferManager::getInstance().release(handle);
}
int mali_gralloc_reference_validate(buffer_handle_t handle) {
return BufferManager::getInstance().validate(handle);
}
std::optional<void *> mali_gralloc_reference_get_buf_addr(buffer_handle_t handle) {
return BufferManager::getInstance().get_buf_addr(handle);
}
std::optional<void *> mali_gralloc_reference_get_metadata_addr(buffer_handle_t handle) {
return BufferManager::getInstance().get_metadata_addr(handle);
}