1 /*
2  * Copyright (c) 2014, Mentor Graphics Corporation
3  * Copyright (c) 2018, Xilinx Inc.
4  * All rights reserved.
5  *
6  * SPDX-License-Identifier: BSD-3-Clause
7  */
8 
9 #include <metal/io.h>
10 #include <metal/utilities.h>
11 #include <openamp/rsc_table_parser.h>
12 
13 static int handle_dummy_rsc(struct remoteproc *rproc, void *rsc);
14 
15 /* Resources handler */
16 static const rsc_handler rsc_handler_table[] = {
17 	handle_carve_out_rsc, /**< carved out resource */
18 	handle_dummy_rsc, /**< IOMMU dev mem resource */
19 	handle_trace_rsc, /**< trace buffer resource */
20 	handle_vdev_rsc, /**< virtio resource */
21 };
22 
handle_rsc_table(struct remoteproc * rproc,struct resource_table * rsc_table,size_t size,struct metal_io_region * io)23 int handle_rsc_table(struct remoteproc *rproc,
24 		     struct resource_table *rsc_table, size_t size,
25 		     struct metal_io_region *io)
26 {
27 	struct fw_rsc_hdr *hdr;
28 	uint32_t rsc_type;
29 	unsigned int idx, offset;
30 	int status = 0;
31 
32 	/* Validate rsc table header fields */
33 
34 	/* Minimum rsc table size */
35 	if (sizeof(struct resource_table) > size) {
36 		return -RPROC_ERR_RSC_TAB_TRUNC;
37 	}
38 
39 	/* Supported version */
40 	if (rsc_table->ver != RSC_TAB_SUPPORTED_VERSION) {
41 		return -RPROC_ERR_RSC_TAB_VER;
42 	}
43 
44 	/* Offset array */
45 	offset = sizeof(struct resource_table)
46 		 + rsc_table->num * sizeof(rsc_table->offset[0]);
47 
48 	if (offset > size) {
49 		return -RPROC_ERR_RSC_TAB_TRUNC;
50 	}
51 
52 	/* Reserved fields - must be zero */
53 	if (rsc_table->reserved[0] != 0 || rsc_table->reserved[1] != 0) {
54 		return -RPROC_ERR_RSC_TAB_RSVD;
55 	}
56 
57 	/* Loop through the offset array and parse each resource entry */
58 	for (idx = 0; idx < rsc_table->num; idx++) {
59 		hdr = (void *)((char *)rsc_table + rsc_table->offset[idx]);
60 		if (io && metal_io_virt_to_offset(io, hdr) == METAL_BAD_OFFSET)
61 			return -RPROC_ERR_RSC_TAB_TRUNC;
62 		rsc_type = hdr->type;
63 		if (rsc_type < RSC_LAST)
64 			status = rsc_handler_table[rsc_type](rproc, hdr);
65 		else if (rsc_type >= RSC_VENDOR_START &&
66 			 rsc_type <= RSC_VENDOR_END)
67 			status = handle_vendor_rsc(rproc, hdr);
68 		if (status == -RPROC_ERR_RSC_TAB_NS) {
69 			status = 0;
70 			continue;
71 		} else if (status) {
72 			break;
73 		}
74 	}
75 
76 	return status;
77 }
78 
handle_carve_out_rsc(struct remoteproc * rproc,void * rsc)79 int handle_carve_out_rsc(struct remoteproc *rproc, void *rsc)
80 {
81 	struct fw_rsc_carveout *carve_rsc = rsc;
82 	metal_phys_addr_t da;
83 	metal_phys_addr_t pa;
84 	size_t size;
85 	unsigned int attribute;
86 
87 	/* Validate resource fields */
88 	if (!carve_rsc) {
89 		return -RPROC_ERR_RSC_TAB_NP;
90 	}
91 
92 	if (carve_rsc->reserved) {
93 		return -RPROC_ERR_RSC_TAB_RSVD;
94 	}
95 	pa = carve_rsc->pa;
96 	da = carve_rsc->da;
97 	size = carve_rsc->len;
98 	attribute = carve_rsc->flags;
99 	if (remoteproc_mmap(rproc, &pa, &da, size, attribute, NULL))
100 		return 0;
101 	else
102 		return -RPROC_EINVAL;
103 }
104 
handle_vendor_rsc(struct remoteproc * rproc,void * rsc)105 int handle_vendor_rsc(struct remoteproc *rproc, void *rsc)
106 {
107 	if (rproc && rproc->ops->handle_rsc) {
108 		struct fw_rsc_vendor *vend_rsc = rsc;
109 		size_t len = vend_rsc->len;
110 
111 		return rproc->ops->handle_rsc(rproc, rsc, len);
112 	}
113 	return -RPROC_ERR_RSC_TAB_NS;
114 }
115 
handle_vdev_rsc(struct remoteproc * rproc,void * rsc)116 int handle_vdev_rsc(struct remoteproc *rproc, void *rsc)
117 {
118 	struct fw_rsc_vdev *vdev_rsc = rsc;
119 	int i, num_vrings;
120 	unsigned int notifyid;
121 	struct fw_rsc_vdev_vring *vring_rsc;
122 
123 	/* only assign notification IDs but do not initialize vdev */
124 	notifyid = vdev_rsc->notifyid;
125 	notifyid = remoteproc_allocate_id(rproc,
126 					  notifyid,
127 					  notifyid == RSC_NOTIFY_ID_ANY ?
128 					  RSC_NOTIFY_ID_ANY : notifyid + 1);
129 	if (notifyid != RSC_NOTIFY_ID_ANY)
130 		vdev_rsc->notifyid = notifyid;
131 	else
132 		return -RPROC_ERR_RSC_TAB_NP;
133 
134 	num_vrings = vdev_rsc->num_of_vrings;
135 	for (i = 0; i < num_vrings; i++) {
136 		vring_rsc = &vdev_rsc->vring[i];
137 		notifyid = vring_rsc->notifyid;
138 		notifyid = remoteproc_allocate_id(rproc,
139 						  notifyid,
140 						  notifyid == RSC_NOTIFY_ID_ANY ?
141 						  RSC_NOTIFY_ID_ANY : notifyid + 1);
142 		if (notifyid != RSC_NOTIFY_ID_ANY)
143 			vring_rsc->notifyid = notifyid;
144 		else
145 			goto err;
146 	}
147 
148 	return 0;
149 
150 err:
151 	for (i--; i >= 0; i--) {
152 		vring_rsc = &vdev_rsc->vring[i];
153 		metal_bitmap_clear_bit(&rproc->bitmap, vring_rsc->notifyid);
154 	}
155 	metal_bitmap_clear_bit(&rproc->bitmap, vdev_rsc->notifyid);
156 
157 	return -RPROC_ERR_RSC_TAB_NP;
158 }
159 
handle_trace_rsc(struct remoteproc * rproc,void * rsc)160 int handle_trace_rsc(struct remoteproc *rproc, void *rsc)
161 {
162 	struct fw_rsc_trace *vdev_rsc = rsc;
163 	(void)rproc;
164 
165 	if (vdev_rsc->da != FW_RSC_U32_ADDR_ANY && vdev_rsc->len != 0)
166 		return 0;
167 	/* FIXME: The host should allocated a memory used by remote */
168 
169 	return -RPROC_ERR_RSC_TAB_NS;
170 }
171 
172 /**
173  * @internal
174  *
175  * @brief Dummy resource handler.
176  *
177  * @param rproc	Pointer to remote remoteproc
178  * @param rsc	Pointer to trace resource
179  *
180  * @return No service error
181  */
handle_dummy_rsc(struct remoteproc * rproc,void * rsc)182 static int handle_dummy_rsc(struct remoteproc *rproc, void *rsc)
183 {
184 	(void)rproc;
185 	(void)rsc;
186 
187 	return -RPROC_ERR_RSC_TAB_NS;
188 }
189 
find_rsc(void * rsc_table,unsigned int rsc_type,unsigned int index)190 size_t find_rsc(void *rsc_table, unsigned int rsc_type, unsigned int index)
191 {
192 	struct resource_table *r_table = rsc_table;
193 	struct fw_rsc_hdr *hdr;
194 	unsigned int i, rsc_index;
195 	unsigned int lrsc_type;
196 
197 	metal_assert(r_table);
198 	if (!r_table)
199 		return 0;
200 
201 	/* Loop through the offset array and parse each resource entry */
202 	rsc_index = 0;
203 	for (i = 0; i < r_table->num; i++) {
204 		hdr = (void *)((char *)r_table + r_table->offset[i]);
205 		lrsc_type = hdr->type;
206 		if (lrsc_type == rsc_type) {
207 			if (rsc_index++ == index)
208 				return r_table->offset[i];
209 		}
210 	}
211 	return 0;
212 }
213