/*
 * Copyright (c) 2014, Mentor Graphics Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. Neither the name of Mentor Graphics Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived from this
 *    software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "openamp/rsc_table_parser.h"
#include "metal/io.h"

/* Resources handler */
rsc_handler rsc_handler_table[] = {
	handle_carve_out_rsc,
	handle_dev_mem_rsc,
	handle_trace_rsc,
	handle_vdev_rsc,
	handle_rproc_mem_rsc,
	handle_fw_chksum_rsc,
	handle_mmu_rsc
};

/**
 * handle_rsc_table
 *
 * This function parses resource table.
 *
 * @param rproc     - pointer to remote remote_proc
 * @param rsc_table - resource table to parse
 * @param size      -  size of rsc table
 *
 * @returns - execution status
 *
 */
int handle_rsc_table(struct remote_proc *rproc,
		     struct resource_table *rsc_table, int size)
{

	unsigned char *rsc_start;
	unsigned int *rsc_offset;
	unsigned int rsc_type;
	unsigned int idx;
	int status = 0;

	/* Validate rsc table header fields */

	/* Minimum rsc table size */
	if (sizeof(struct resource_table) > (unsigned int)size) {
		return (RPROC_ERR_RSC_TAB_TRUNC);
	}

	/* Supported version */
	if (rsc_table->ver != RSC_TAB_SUPPORTED_VERSION) {
		return (RPROC_ERR_RSC_TAB_VER);
	}

	/* Offset array */
	if (sizeof(struct resource_table)
	    + rsc_table->num * sizeof(rsc_table->offset[0]) > (unsigned int)size) {
		return (RPROC_ERR_RSC_TAB_TRUNC);
	}

	/* Reserved fields - must be zero */
	if ((rsc_table->reserved[0] != 0 || rsc_table->reserved[1]) != 0) {
		return RPROC_ERR_RSC_TAB_RSVD;
	}

	rsc_start = (unsigned char *)rsc_table;

	/* Loop through the offset array and parse each resource entry */
	for (idx = 0; idx < rsc_table->num; idx++) {
		rsc_offset =
		    (unsigned int *)(rsc_start + rsc_table->offset[idx]);
		rsc_type = *rsc_offset;
		status =
		    rsc_handler_table[rsc_type] (rproc, (void *)rsc_offset);
		if (status != RPROC_SUCCESS) {
			break;
		}
	}

	return status;
}

/**
 * handle_carve_out_rsc
 *
 * Carveout resource handler.
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to carveout resource
 *
 * @returns - execution status
 *
 */
int handle_carve_out_rsc(struct remote_proc *rproc, void *rsc)
{
	struct fw_rsc_carveout *carve_rsc = (struct fw_rsc_carveout *)rsc;

	/* Validate resource fields */
	if (!carve_rsc) {
		return RPROC_ERR_RSC_TAB_NP;
	}

	if (carve_rsc->reserved) {
		return RPROC_ERR_RSC_TAB_RSVD;
	}

	if (rproc->role == RPROC_MASTER) {
		/* Map memory region for loading the image */
		/* Use generic I/O region here. This is a temporary solution.
		 * It will not work in Linux userspace. It is not decided if use
		 * linux kernel remmoteproc to load the firmware or do it in user
		 * space. */
		metal_phys_addr_t carve_out_generic_base_addr = 0;
		struct metal_io_region generic_io = {
			(void *)0,
			&carve_out_generic_base_addr,
			(size_t)-1,
			(sizeof(metal_phys_addr_t) << 3),
			(unsigned long)(-1),
			METAL_UNCACHED | METAL_SHARED_MEM,
			{NULL},
		};
		metal_io_mem_map((metal_phys_addr_t)carve_rsc->da,
			&generic_io, carve_rsc->len);
	}

	return RPROC_SUCCESS;
}

/**
 * handle_trace_rsc
 *
 * Trace resource handler.
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to trace resource
 *
 * @returns - execution status
 *
 */
int handle_trace_rsc(struct remote_proc *rproc, void *rsc)
{
	(void)rproc;
	(void)rsc;

	return RPROC_ERR_RSC_TAB_NS;
}

/**
 * handle_dev_mem_rsc
 *
 * Device memory resource handler.
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to device memory resource
 *
 * @returns - execution status
 *
 */
int handle_dev_mem_rsc(struct remote_proc *rproc, void *rsc)
{
	(void)rproc;
	(void)rsc;

	return RPROC_ERR_RSC_TAB_NS;
}

/**
 * handle_vdev_rsc
 *
 * Virtio device resource handler
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to virtio device resource
 *
 * @returns - execution status
 *
 */
int handle_vdev_rsc(struct remote_proc *rproc, void *rsc)
{

	struct fw_rsc_vdev *vdev_rsc = (struct fw_rsc_vdev *)rsc;
	struct proc_vdev *vdev;

	if (!vdev_rsc) {
		return RPROC_ERR_RSC_TAB_NP;
	}

	/* Maximum supported vrings per Virtio device */
	if (vdev_rsc->num_of_vrings > RSC_TAB_MAX_VRINGS) {
		return RPROC_ERR_RSC_TAB_VDEV_NRINGS;
	}

	/* Reserved fields - must be zero */
	if (vdev_rsc->reserved[0] || vdev_rsc->reserved[1]) {
		return RPROC_ERR_RSC_TAB_RSVD;
	}

	/* Get the Virtio device from HIL proc */
	vdev = hil_get_vdev_info(rproc->proc);

	/* Initialize HIL Virtio device resources */
	vdev->num_vrings = vdev_rsc->num_of_vrings;
	vdev->dfeatures = vdev_rsc->dfeatures;
	vdev->gfeatures = vdev_rsc->gfeatures;
	vdev->vdev_info = vdev_rsc;

	/* Map virtio memory as I/O memory */
	if (vdev->io) {
		metal_phys_addr_t vdev_pa =
			metal_io_virt_to_phys(vdev->io, vdev_rsc);
		vdev->vdev_info = metal_io_mem_map(vdev_pa,
			vdev->io, sizeof(*vdev_rsc));
	} else {
		vdev->vdev_info = vdev_rsc;
	}

	return RPROC_SUCCESS;
}

/**
 * handle_rproc_mem_rsc
 *
 * This function parses rproc_mem resource.
 * This is the resource for the remote processor
 * to tell the host the memory can be used as
 * shared memory.
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to mmu resource
 *
 * @returns - execution status
 *
 */
int handle_rproc_mem_rsc(struct remote_proc *rproc, void *rsc)
{
	(void)rproc;
	(void)rsc;

	/* TODO: the firmware side should handle this resource properly
	 * when it is the master or when it is the remote. */
	return RPROC_SUCCESS;
}

/*
 * handle_fw_chksum_rsc
 *
 * This function parses firmware checksum resource.
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to mmu resource
 *
 * @returns - execution status
 *
 */
int handle_fw_chksum_rsc(struct remote_proc *rproc, void *rsc)
{
	(void)rproc;
	(void)rsc;

	/* TODO: the firmware side should handle this resource properly
	 * when it is the master or when it is the remote. */
	return RPROC_SUCCESS;
}

/**
 * handle_mmu_rsc
 *
 * This function parses mmu resource , requested by the peripheral.
 *
 * @param rproc - pointer to remote remote_proc
 * @param rsc   - pointer to mmu resource
 *
 * @returns - execution status
 *
 */
int handle_mmu_rsc(struct remote_proc *rproc, void *rsc)
{
	(void)rproc;
	(void)rsc;

	return RPROC_ERR_RSC_TAB_NS;
}
