1  // SPDX-License-Identifier: GPL-2.0-only OR MIT
2  /* Copyright (c) 2023 Imagination Technologies Ltd. */
3  
4  #include "pvr_device.h"
5  #include "pvr_rogue_fwif_stream.h"
6  #include "pvr_stream.h"
7  
8  #include <linux/align.h>
9  #include <linux/slab.h>
10  #include <linux/types.h>
11  #include <uapi/drm/pvr_drm.h>
12  
13  static __always_inline bool
stream_def_is_supported(struct pvr_device * pvr_dev,const struct pvr_stream_def * stream_def)14  stream_def_is_supported(struct pvr_device *pvr_dev, const struct pvr_stream_def *stream_def)
15  {
16  	if (stream_def->feature == PVR_FEATURE_NONE)
17  		return true;
18  
19  	if (!(stream_def->feature & PVR_FEATURE_NOT) &&
20  	    pvr_device_has_feature(pvr_dev, stream_def->feature)) {
21  		return true;
22  	}
23  
24  	if ((stream_def->feature & PVR_FEATURE_NOT) &&
25  	    !pvr_device_has_feature(pvr_dev, stream_def->feature & ~PVR_FEATURE_NOT)) {
26  		return true;
27  	}
28  
29  	return false;
30  }
31  
32  static int
pvr_stream_get_data(u8 * stream,u32 * stream_offset,u32 stream_size,u32 data_size,u32 align_size,void * dest)33  pvr_stream_get_data(u8 *stream, u32 *stream_offset, u32 stream_size, u32 data_size, u32 align_size,
34  		    void *dest)
35  {
36  	*stream_offset = ALIGN(*stream_offset, align_size);
37  
38  	if ((*stream_offset + data_size) > stream_size)
39  		return -EINVAL;
40  
41  	memcpy(dest, stream + *stream_offset, data_size);
42  
43  	(*stream_offset) += data_size;
44  
45  	return 0;
46  }
47  
48  /**
49   * pvr_stream_process_1() - Process a single stream and fill destination structure
50   * @pvr_dev: Device pointer.
51   * @stream_def: Stream definition.
52   * @nr_entries: Number of entries in &stream_def.
53   * @stream: Pointer to stream.
54   * @stream_offset: Starting offset within stream.
55   * @stream_size: Size of input stream, in bytes.
56   * @dest: Pointer to destination structure.
57   * @dest_size: Size of destination structure.
58   * @stream_offset_out: Pointer to variable to write updated stream offset to. May be NULL.
59   *
60   * Returns:
61   *  * 0 on success, or
62   *  * -%EINVAL on malformed stream.
63   */
64  static int
pvr_stream_process_1(struct pvr_device * pvr_dev,const struct pvr_stream_def * stream_def,u32 nr_entries,u8 * stream,u32 stream_offset,u32 stream_size,u8 * dest,u32 dest_size,u32 * stream_offset_out)65  pvr_stream_process_1(struct pvr_device *pvr_dev, const struct pvr_stream_def *stream_def,
66  		     u32 nr_entries, u8 *stream, u32 stream_offset, u32 stream_size,
67  		     u8 *dest, u32 dest_size, u32 *stream_offset_out)
68  {
69  	int err = 0;
70  	u32 i;
71  
72  	for (i = 0; i < nr_entries; i++) {
73  		if (stream_def[i].offset >= dest_size) {
74  			err = -EINVAL;
75  			break;
76  		}
77  
78  		if (!stream_def_is_supported(pvr_dev, &stream_def[i]))
79  			continue;
80  
81  		switch (stream_def[i].size) {
82  		case PVR_STREAM_SIZE_8:
83  			err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u8),
84  						  sizeof(u8), dest + stream_def[i].offset);
85  			if (err)
86  				return err;
87  			break;
88  
89  		case PVR_STREAM_SIZE_16:
90  			err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u16),
91  						  sizeof(u16), dest + stream_def[i].offset);
92  			if (err)
93  				return err;
94  			break;
95  
96  		case PVR_STREAM_SIZE_32:
97  			err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32),
98  						  sizeof(u32), dest + stream_def[i].offset);
99  			if (err)
100  				return err;
101  			break;
102  
103  		case PVR_STREAM_SIZE_64:
104  			err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u64),
105  						  sizeof(u64), dest + stream_def[i].offset);
106  			if (err)
107  				return err;
108  			break;
109  
110  		case PVR_STREAM_SIZE_ARRAY:
111  			err = pvr_stream_get_data(stream, &stream_offset, stream_size,
112  						  stream_def[i].array_size, sizeof(u64),
113  						  dest + stream_def[i].offset);
114  			if (err)
115  				return err;
116  			break;
117  		}
118  	}
119  
120  	if (stream_offset_out)
121  		*stream_offset_out = stream_offset;
122  
123  	return err;
124  }
125  
126  static int
pvr_stream_process_ext_stream(struct pvr_device * pvr_dev,const struct pvr_stream_cmd_defs * cmd_defs,void * ext_stream,u32 stream_offset,u32 ext_stream_size,void * dest)127  pvr_stream_process_ext_stream(struct pvr_device *pvr_dev,
128  			      const struct pvr_stream_cmd_defs *cmd_defs, void *ext_stream,
129  			      u32 stream_offset, u32 ext_stream_size, void *dest)
130  {
131  	u32 musthave_masks[PVR_STREAM_EXTHDR_TYPE_MAX];
132  	u32 ext_header;
133  	int err = 0;
134  	u32 i;
135  
136  	/* Copy "must have" mask from device. We clear this as we process the stream. */
137  	memcpy(musthave_masks, pvr_dev->stream_musthave_quirks[cmd_defs->type],
138  	       sizeof(musthave_masks));
139  
140  	do {
141  		const struct pvr_stream_ext_header *header;
142  		u32 type;
143  		u32 data;
144  
145  		err = pvr_stream_get_data(ext_stream, &stream_offset, ext_stream_size, sizeof(u32),
146  					  sizeof(ext_header), &ext_header);
147  		if (err)
148  			return err;
149  
150  		type = (ext_header & PVR_STREAM_EXTHDR_TYPE_MASK) >> PVR_STREAM_EXTHDR_TYPE_SHIFT;
151  		data = ext_header & PVR_STREAM_EXTHDR_DATA_MASK;
152  
153  		if (type >= cmd_defs->ext_nr_headers)
154  			return -EINVAL;
155  
156  		header = &cmd_defs->ext_headers[type];
157  		if (data & ~header->valid_mask)
158  			return -EINVAL;
159  
160  		musthave_masks[type] &= ~data;
161  
162  		for (i = 0; i < header->ext_streams_num; i++) {
163  			const struct pvr_stream_ext_def *ext_def = &header->ext_streams[i];
164  
165  			if (!(ext_header & ext_def->header_mask))
166  				continue;
167  
168  			if (!pvr_device_has_uapi_quirk(pvr_dev, ext_def->quirk))
169  				return -EINVAL;
170  
171  			err = pvr_stream_process_1(pvr_dev, ext_def->stream, ext_def->stream_len,
172  						   ext_stream, stream_offset,
173  						   ext_stream_size, dest,
174  						   cmd_defs->dest_size, &stream_offset);
175  			if (err)
176  				return err;
177  		}
178  	} while (ext_header & PVR_STREAM_EXTHDR_CONTINUATION);
179  
180  	/*
181  	 * Verify that "must have" mask is now zero. If it isn't then one of the "must have" quirks
182  	 * for this command was not present.
183  	 */
184  	for (i = 0; i < cmd_defs->ext_nr_headers; i++) {
185  		if (musthave_masks[i])
186  			return -EINVAL;
187  	}
188  
189  	return 0;
190  }
191  
192  /**
193   * pvr_stream_process() - Build FW structure from stream
194   * @pvr_dev: Device pointer.
195   * @cmd_defs: Stream definition.
196   * @stream: Pointer to command stream.
197   * @stream_size: Size of command stream, in bytes.
198   * @dest_out: Pointer to destination buffer.
199   *
200   * Caller is responsible for freeing the output structure.
201   *
202   * Returns:
203   *  * 0 on success,
204   *  * -%ENOMEM on out of memory, or
205   *  * -%EINVAL on malformed stream.
206   */
207  int
pvr_stream_process(struct pvr_device * pvr_dev,const struct pvr_stream_cmd_defs * cmd_defs,void * stream,u32 stream_size,void * dest_out)208  pvr_stream_process(struct pvr_device *pvr_dev, const struct pvr_stream_cmd_defs *cmd_defs,
209  		   void *stream, u32 stream_size, void *dest_out)
210  {
211  	u32 stream_offset = 0;
212  	u32 main_stream_len;
213  	u32 padding;
214  	int err;
215  
216  	if (!stream || !stream_size)
217  		return -EINVAL;
218  
219  	err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32),
220  				  sizeof(u32), &main_stream_len);
221  	if (err)
222  		return err;
223  
224  	/*
225  	 * u32 after stream length is padding to ensure u64 alignment, but may be used for expansion
226  	 * in the future. Verify it's zero.
227  	 */
228  	err = pvr_stream_get_data(stream, &stream_offset, stream_size, sizeof(u32),
229  				  sizeof(u32), &padding);
230  	if (err)
231  		return err;
232  
233  	if (main_stream_len < stream_offset || main_stream_len > stream_size || padding)
234  		return -EINVAL;
235  
236  	err = pvr_stream_process_1(pvr_dev, cmd_defs->main_stream, cmd_defs->main_stream_len,
237  				   stream, stream_offset, main_stream_len, dest_out,
238  				   cmd_defs->dest_size, &stream_offset);
239  	if (err)
240  		return err;
241  
242  	if (stream_offset < stream_size) {
243  		err = pvr_stream_process_ext_stream(pvr_dev, cmd_defs, stream, stream_offset,
244  						    stream_size, dest_out);
245  		if (err)
246  			return err;
247  	} else {
248  		u32 i;
249  
250  		/*
251  		 * If we don't have an extension stream then there must not be any "must have"
252  		 * quirks for this command.
253  		 */
254  		for (i = 0; i < cmd_defs->ext_nr_headers; i++) {
255  			if (pvr_dev->stream_musthave_quirks[cmd_defs->type][i])
256  				return -EINVAL;
257  		}
258  	}
259  
260  	return 0;
261  }
262  
263  /**
264   * pvr_stream_create_musthave_masks() - Create "must have" masks for streams based on current device
265   *                                      quirks
266   * @pvr_dev: Device pointer.
267   */
268  void
pvr_stream_create_musthave_masks(struct pvr_device * pvr_dev)269  pvr_stream_create_musthave_masks(struct pvr_device *pvr_dev)
270  {
271  	memset(pvr_dev->stream_musthave_quirks, 0, sizeof(pvr_dev->stream_musthave_quirks));
272  
273  	if (pvr_device_has_uapi_quirk(pvr_dev, 47217))
274  		pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_FRAG][0] |=
275  			PVR_STREAM_EXTHDR_FRAG0_BRN47217;
276  
277  	if (pvr_device_has_uapi_quirk(pvr_dev, 49927)) {
278  		pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_GEOM][0] |=
279  			PVR_STREAM_EXTHDR_GEOM0_BRN49927;
280  		pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_FRAG][0] |=
281  			PVR_STREAM_EXTHDR_FRAG0_BRN49927;
282  		pvr_dev->stream_musthave_quirks[PVR_STREAM_TYPE_COMPUTE][0] |=
283  			PVR_STREAM_EXTHDR_COMPUTE0_BRN49927;
284  	}
285  }
286