blob: e9ba2ebfc860f6466380c9311cdb1c68e757c524 [file] [log] [blame]
/*
* Copyright (C) 2017 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 "gralloc_vsoc_priv.h"
#include <unistd.h>
#include <string.h>
#include <sys/mman.h>
#include <cutils/hashmap.h>
#include <hardware/gralloc.h>
#include <hardware/hardware.h>
#include <log/log.h>
namespace {
const size_t g_page_size = sysconf(_SC_PAGESIZE);
struct HmLockGuard {
HmLockGuard(Hashmap* map) : map_(map) {
hashmapLock(map_);
}
~HmLockGuard() {
hashmapUnlock(map_);
}
private:
Hashmap* map_;
};
int offset_hash(void* key) {
return *reinterpret_cast<int*>(key);
}
bool offset_equals(void* key1, void* key2) {
return *reinterpret_cast<int*>(key1) ==
*reinterpret_cast<int*>(key2);
}
// Keeps track of how many times a buffer is locked in the current process.
struct GrallocBuffer {
void* vaddr;
int ref_count;
GrallocBuffer() : vaddr(NULL), ref_count(0) {}
static Hashmap* mapped_buffers() {
static Hashmap* mapped_buffers =
hashmapCreate(19, offset_hash, offset_equals);
return mapped_buffers;
}
};
}
void* reference_buffer(const vsoc_buffer_handle_t* hnd) {
Hashmap* map = GrallocBuffer::mapped_buffers();
HmLockGuard lock_guard(map);
GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
hashmapGet(map, const_cast<int*>(&hnd->offset)));
if (!buffer) {
buffer = new GrallocBuffer();
hashmapPut(map, const_cast<int*>(&hnd->offset), buffer);
}
if (!buffer->vaddr) {
void* mapped =
mmap(NULL, hnd->size, PROT_READ | PROT_WRITE, MAP_SHARED, hnd->fd, 0);
if (mapped == MAP_FAILED) {
ALOGE("Unable to map buffer (offset: %d, size: %d): %s",
hnd->offset,
hnd->size,
strerror(errno));
return NULL;
}
// Set up the guard pages. The last page is always a guard
uintptr_t base = uintptr_t(mapped);
uintptr_t addr = base + hnd->size - g_page_size;
if (mprotect((void*)addr, g_page_size, PROT_NONE) == -1) {
ALOGW("Unable to protect last page of buffer (offset: %d, size: %d): %s",
hnd->offset,
hnd->size,
strerror(errno));
}
buffer->vaddr = mapped;
}
buffer->ref_count++;
return buffer->vaddr;
}
int unreference_buffer(const vsoc_buffer_handle_t* hnd) {
int result = 0;
Hashmap* map = GrallocBuffer::mapped_buffers();
HmLockGuard lock_guard(map);
GrallocBuffer* buffer = reinterpret_cast<GrallocBuffer*>(
hashmapGet(map, const_cast<int*>(&hnd->offset)));
if (!buffer) {
ALOGE("Unreferencing an unknown buffer (offset: %d, size: %d)",
hnd->offset,
hnd->size);
return -EINVAL;
}
if (buffer->ref_count == 0) {
ALOGE("Unbalanced reference/unreference on buffer (offset: %d, size: %d)",
hnd->offset,
hnd->size);
return -EINVAL;
}
buffer->ref_count--;
if (buffer->ref_count == 0) {
result = munmap(buffer->vaddr, hnd->size);
if (result) {
ALOGE("Unable to unmap buffer (offset: %d, size: %d): %s",
hnd->offset,
hnd->size,
strerror(errno));
}
buffer->vaddr = NULL;
}
return result;
}