/* * Copyright (c) 2014, Mentor Graphics Corporation * Copyright (c) 2018, Xilinx Inc. * All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include #include 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; }