brintos

brintos / linux-shallow public Read only

0
0
Text · 7.7 KiB · 975336a Raw
286 lines · c
1// SPDX-License-Identifier: GPL-2.0-only OR MIT2/* 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 13static __always_inline bool14stream_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 32static int33pvr_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 structure50 * @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, or62 *  * -%EINVAL on malformed stream.63 */64static int65pvr_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 126static int127pvr_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" quirks182	 * 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 stream194 * @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, or205 *  * -%EINVAL on malformed stream.206 */207int208pvr_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 expansion226	 * 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 device265 *                                      quirks266 * @pvr_dev: Device pointer.267 */268void269pvr_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