blob: 37c6478011f0a8dc17c967bba6b83cd2a34cd3dc [file] [log] [blame]
/*
* Copyright (c) 2014, Mentor Graphics Corporation
* Copyright (c) 2018, Xilinx Inc.
* All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <metal/io.h>
#include <openamp/rsc_table_parser.h>
static int handle_dummy_rsc(struct remoteproc *rproc, void *rsc);
/* Resources handler */
rsc_handler rsc_handler_table[] = {
handle_carve_out_rsc, /**< carved out resource */
handle_dummy_rsc, /**< IOMMU dev mem resource */
handle_trace_rsc, /**< trace buffer resource */
handle_vdev_rsc, /**< virtio resource */
handle_dummy_rsc, /**< rproc shared memory resource */
handle_dummy_rsc, /**< firmware checksum resource */
};
int handle_rsc_table(struct remoteproc *rproc,
struct resource_table *rsc_table, int size,
struct metal_io_region *io)
{
char *rsc_start;
unsigned int rsc_type;
unsigned int idx, offset;
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 */
offset = sizeof(struct resource_table)
+ rsc_table->num * sizeof(rsc_table->offset[0]);
if (offset > (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;
}
/* Loop through the offset array and parse each resource entry */
for (idx = 0; idx < rsc_table->num; idx++) {
rsc_start = (char *)rsc_table;
rsc_start += rsc_table->offset[idx];
if (io &&
metal_io_virt_to_offset(io, rsc_start) == METAL_BAD_OFFSET)
return -RPROC_ERR_RSC_TAB_TRUNC;
rsc_type = *((uint32_t *)rsc_start);
if (rsc_type < RSC_LAST)
status = rsc_handler_table[rsc_type](rproc,
rsc_start);
else if (rsc_type >= RSC_VENDOR_START &&
rsc_type <= RSC_VENDOR_END)
status = handle_vendor_rsc(rproc, rsc_start);
if (status == -RPROC_ERR_RSC_TAB_NS) {
status = 0;
continue;
}
else if (status)
break;
}
return status;
}
/**
* handle_carve_out_rsc
*
* Carveout resource handler.
*
* @param rproc - pointer to remote remoteproc
* @param rsc - pointer to carveout resource
*
* @returns - 0 for success, or negative value for failure
*
*/
int handle_carve_out_rsc(struct remoteproc *rproc, void *rsc)
{
struct fw_rsc_carveout *carve_rsc = (struct fw_rsc_carveout *)rsc;
metal_phys_addr_t da;
metal_phys_addr_t pa;
size_t size;
unsigned int attribute;
/* Validate resource fields */
if (!carve_rsc) {
return -RPROC_ERR_RSC_TAB_NP;
}
if (carve_rsc->reserved) {
return -RPROC_ERR_RSC_TAB_RSVD;
}
pa = carve_rsc->pa;
da = carve_rsc->da;
size = carve_rsc->len;
attribute = carve_rsc->flags;
if (remoteproc_mmap(rproc, &pa, &da, size, attribute, NULL))
return 0;
else
return -RPROC_EINVAL;
}
int handle_vendor_rsc(struct remoteproc *rproc, void *rsc)
{
if (rproc && rproc->ops->handle_rsc) {
struct fw_rsc_vendor *vend_rsc = rsc;
size_t len = vend_rsc->len;
return rproc->ops->handle_rsc(rproc, rsc, len);
}
return -RPROC_ERR_RSC_TAB_NS;
}
int handle_vdev_rsc(struct remoteproc *rproc, void *rsc)
{
struct fw_rsc_vdev *vdev_rsc = (struct fw_rsc_vdev *)rsc;
unsigned int notifyid, i, num_vrings;
/* only assign notification IDs but do not initialize vdev */
notifyid = vdev_rsc->notifyid;
if (notifyid == RSC_NOTIFY_ID_ANY) {
notifyid = remoteproc_allocate_id(rproc,
notifyid, notifyid + 1);
vdev_rsc->notifyid = notifyid;
}
num_vrings = vdev_rsc->num_of_vrings;
for (i = 0; i < num_vrings; i++) {
struct fw_rsc_vdev_vring *vring_rsc;
vring_rsc = &vdev_rsc->vring[i];
notifyid = vring_rsc->notifyid;
if (notifyid == RSC_NOTIFY_ID_ANY) {
notifyid = remoteproc_allocate_id(rproc,
notifyid,
notifyid + 1);
vdev_rsc->notifyid = notifyid;
}
}
return 0;
}
/**
* handle_trace_rsc
*
* trace resource handler.
*
* @param rproc - pointer to remote remoteproc
* @param rsc - pointer to trace resource
*
* @returns - no service error
*
*/
int handle_trace_rsc(struct remoteproc *rproc, void *rsc)
{
struct fw_rsc_trace *vdev_rsc = (struct fw_rsc_trace *)rsc;
(void)rproc;
if (vdev_rsc->da != FW_RSC_U32_ADDR_ANY && vdev_rsc->len != 0)
return 0;
/* FIXME: master should allocated a memory used by slave */
return -RPROC_ERR_RSC_TAB_NS;
}
/**
* handle_dummy_rsc
*
* dummy resource handler.
*
* @param rproc - pointer to remote remoteproc
* @param rsc - pointer to trace resource
*
* @returns - no service error
*
*/
static int handle_dummy_rsc(struct remoteproc *rproc, void *rsc)
{
(void)rproc;
(void)rsc;
return -RPROC_ERR_RSC_TAB_NS;
}
size_t find_rsc(void *rsc_table, unsigned int rsc_type, unsigned int index)
{
struct resource_table *r_table = rsc_table;
unsigned int i, rsc_index;
unsigned int lrsc_type;
char *rsc_start;
metal_assert(r_table);
/* Loop through the offset array and parse each resource entry */
rsc_index = 0;
for (i = 0; i < r_table->num; i++) {
rsc_start = (char *)r_table;
rsc_start += r_table->offset[i];
lrsc_type = *((uint32_t *)rsc_start);
if (lrsc_type == rsc_type) {
if (rsc_index++ == index)
return r_table->offset[i];
}
}
return 0;
}