/*
 * Copyright (C) 2014-2017 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 <cutils/ashmem.h>
#include <log/log.h>
#include <sys/mman.h>

#if GRALLOC_USE_GRALLOC1_API == 1
#include <hardware/gralloc1.h>
#else
#include <hardware/gralloc.h>
#endif

#include "mali_gralloc_module.h"
#include "mali_gralloc_private_interface_types.h"
#include "mali_gralloc_buffer.h"
#include "gralloc_buffer_priv.h"

/*
 * Allocate shared memory for attribute storage. Only to be
 * used by gralloc internally.
 *
 * Return 0 on success.
 */
int gralloc_buffer_attr_allocate(private_handle_t *hnd)
{
	int rval = -1;

	if (!hnd)
	{
		goto out;
	}

	if (hnd->share_attr_fd >= 0)
	{
		ALOGW("Warning share attribute fd already exists during create. Closing.");
		close(hnd->share_attr_fd);
	}

	hnd->share_attr_fd = ashmem_create_region("gralloc_shared_attr", PAGE_SIZE);

	if (hnd->share_attr_fd < 0)
	{
		ALOGE("Failed to allocate page for shared attribute region");
		goto err_ashmem;
	}

	/*
	 * Default protection on the shm region is PROT_EXEC | PROT_READ | PROT_WRITE.
	 *
	 * Personality flag READ_IMPLIES_EXEC which is used by some processes, namely gdbserver,
	 * causes a mmap with PROT_READ to be translated to PROT_READ | PROT_EXEC.
	 *
	 * If we were to drop PROT_EXEC here with a call to ashmem_set_prot_region()
	 * this can potentially cause clients to fail importing this gralloc attribute buffer
	 * with EPERM error since PROT_EXEC is not allowed.
	 *
	 * Because of this we keep the PROT_EXEC flag.
	 */

	hnd->attr_base = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, hnd->share_attr_fd, 0);

	if (hnd->attr_base != MAP_FAILED)
	{
		/* The attribute region contains signed integers only.
		 * The reason for this is because we can set a value less than 0 for
		 * not-initialized values.
		 */
		attr_region *region = (attr_region *)hnd->attr_base;

		memset(hnd->attr_base, 0xff, PAGE_SIZE);
		munmap(hnd->attr_base, PAGE_SIZE);
		hnd->attr_base = MAP_FAILED;
	}
	else
	{
		ALOGE("Failed to mmap shared attribute region");
		goto err_ashmem;
	}

	rval = 0;
	goto out;

err_ashmem:

	if (hnd->share_attr_fd >= 0)
	{
		close(hnd->share_attr_fd);
		hnd->share_attr_fd = -1;
	}

out:
	return rval;
}

/*
 * Frees the shared memory allocated for attribute storage.
 * Only to be used by gralloc internally.

 * Return 0 on success.
 */
int gralloc_buffer_attr_free(private_handle_t *hnd)
{
	int rval = -1;

	if (!hnd)
	{
		goto out;
	}

	if (hnd->share_attr_fd < 0)
	{
		ALOGE("Shared attribute region not avail to free");
		goto out;
	}

	if (hnd->attr_base != MAP_FAILED)
	{
		ALOGW("Warning shared attribute region mapped at free. Unmapping");
		munmap(hnd->attr_base, PAGE_SIZE);
		hnd->attr_base = MAP_FAILED;
	}

	close(hnd->share_attr_fd);
	hnd->share_attr_fd = -1;
	rval = 0;

out:
	return rval;
}