brintos

brintos / linux-shallow public Read only

0
0
Text · 10.0 KiB · 715b21a Raw
363 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2013--2024 Intel Corporation4 */5 6#include <linux/bitfield.h>7#include <linux/bits.h>8#include <linux/err.h>9#include <linux/dma-mapping.h>10#include <linux/gfp_types.h>11#include <linux/math64.h>12#include <linux/sizes.h>13#include <linux/types.h>14 15#include "ipu6.h"16#include "ipu6-bus.h"17#include "ipu6-cpd.h"18 19/* 15 entries + header*/20#define MAX_PKG_DIR_ENT_CNT		1621/* 2 qword per entry/header */22#define PKG_DIR_ENT_LEN			223/* PKG_DIR size in bytes */24#define PKG_DIR_SIZE			((MAX_PKG_DIR_ENT_CNT) *	\25					 (PKG_DIR_ENT_LEN) * sizeof(u64))26/* _IUPKDR_ */27#define PKG_DIR_HDR_MARK		0x5f4955504b44525fULL28 29/* $CPD */30#define CPD_HDR_MARK			0x4450432431 32#define MAX_MANIFEST_SIZE		(SZ_2K * sizeof(u32))33#define MAX_METADATA_SIZE		SZ_64K34 35#define MAX_COMPONENT_ID		12736#define MAX_COMPONENT_VERSION		0xffff37 38#define MANIFEST_IDX	039#define METADATA_IDX	140#define MODULEDATA_IDX	241/*42 * PKG_DIR Entry (type == id)43 * 63:56        55      54:48   47:32   31:24   23:044 * Rsvd         Rsvd    Type    Version Rsvd    Size45 */46#define PKG_DIR_SIZE_MASK	GENMASK(23, 0)47#define PKG_DIR_VERSION_MASK	GENMASK(47, 32)48#define PKG_DIR_TYPE_MASK	GENMASK(54, 48)49 50static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,51							    u8 idx)52{53	const struct ipu6_cpd_hdr *cpd_hdr = cpd;54	const struct ipu6_cpd_ent *ent;55 56	ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);57	return ent + idx;58}59 60#define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)61#define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)62#define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)63 64static const struct ipu6_cpd_metadata_cmpnt_hdr *65ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,66			    unsigned int metadata_size, u8 idx)67{68	size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);69	size_t cmpnt_count = metadata_size - extn_size;70 71	cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);72 73	if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {74		dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",75			idx);76		return ERR_PTR(-EINVAL);77	}78 79	return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;80}81 82static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,83					   const void *metadata,84					   unsigned int metadata_size, u8 idx)85{86	const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;87 88	cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);89	if (IS_ERR(cmpnt))90		return PTR_ERR(cmpnt);91 92	return cmpnt->ver;93}94 95static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,96					  const void *metadata,97					  unsigned int metadata_size, u8 idx)98{99	const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;100 101	cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);102	if (IS_ERR(cmpnt))103		return PTR_ERR(cmpnt);104 105	return cmpnt->id;106}107 108static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,109				      const void *module_data,110				      unsigned int module_data_size,111				      dma_addr_t dma_addr_module_data,112				      u64 *pkg_dir, const void *metadata,113				      unsigned int metadata_size)114{115	const struct ipu6_cpd_module_data_hdr *module_data_hdr;116	const struct ipu6_cpd_hdr *dir_hdr;117	const struct ipu6_cpd_ent *dir_ent;118	unsigned int i;119	u8 len;120 121	if (!module_data)122		return -EINVAL;123 124	module_data_hdr = module_data;125	dir_hdr = module_data + module_data_hdr->hdr_len;126	len = dir_hdr->hdr_len;127	dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);128 129	pkg_dir[0] = PKG_DIR_HDR_MARK;130	/* pkg_dir entry count = component count + pkg_dir header */131	pkg_dir[1] = dir_hdr->ent_cnt + 1;132 133	for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {134		u64 *p = &pkg_dir[PKG_DIR_ENT_LEN *  (1 + i)];135		int ver, id;136 137		*p++ = dma_addr_module_data + dir_ent->offset;138		id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,139						    metadata_size, i);140		if (id < 0 || id > MAX_COMPONENT_ID) {141			dev_err(&isp->pdev->dev, "Invalid CPD component id\n");142			return -EINVAL;143		}144 145		ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,146						      metadata_size, i);147		if (ver < 0 || ver > MAX_COMPONENT_VERSION) {148			dev_err(&isp->pdev->dev,149				"Invalid CPD component version\n");150			return -EINVAL;151		}152 153		*p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |154			FIELD_PREP(PKG_DIR_TYPE_MASK, id) |155			FIELD_PREP(PKG_DIR_VERSION_MASK, ver);156	}157 158	return 0;159}160 161int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)162{163	dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);164	const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;165	struct device *dev = &adev->auxdev.dev;166	struct ipu6_device *isp = adev->isp;167	unsigned int man_sz, met_sz;168	void *pkg_dir_pos;169	int ret;170 171	man_ent = ipu6_cpd_get_manifest(src);172	man_sz = man_ent->len;173 174	met_ent = ipu6_cpd_get_metadata(src);175	met_sz = met_ent->len;176 177	adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;178	adev->pkg_dir = dma_alloc_attrs(dev, adev->pkg_dir_size,179					&adev->pkg_dir_dma_addr, GFP_KERNEL, 0);180	if (!adev->pkg_dir)181		return -ENOMEM;182 183	/*184	 * pkg_dir entry/header:185	 * qword | 63:56 | 55   | 54:48 | 47:32 | 31:24 | 23:0186	 * N         Address/Offset/"_IUPKDR_"187	 * N + 1 | rsvd  | rsvd | type  | ver   | rsvd  | size188	 *189	 * We can ignore other fields that size in N + 1 qword as they190	 * are 0 anyway. Just setting size for now.191	 */192 193	ent = ipu6_cpd_get_moduledata(src);194 195	ret = ipu6_cpd_parse_module_data(isp, src + ent->offset,196					 ent->len, dma_addr_src + ent->offset,197					 adev->pkg_dir, src + met_ent->offset,198					 met_ent->len);199	if (ret) {200		dev_err(&isp->pdev->dev, "Failed to parse module data\n");201		dma_free_attrs(dev, adev->pkg_dir_size,202			       adev->pkg_dir, adev->pkg_dir_dma_addr, 0);203		return ret;204	}205 206	/* Copy manifest after pkg_dir */207	pkg_dir_pos = adev->pkg_dir + PKG_DIR_ENT_LEN * MAX_PKG_DIR_ENT_CNT;208	memcpy(pkg_dir_pos, src + man_ent->offset, man_sz);209 210	/* Copy metadata after manifest */211	pkg_dir_pos += man_sz;212	memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);213 214	dma_sync_single_range_for_device(dev, adev->pkg_dir_dma_addr,215					 0, adev->pkg_dir_size, DMA_TO_DEVICE);216 217	return 0;218}219EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);220 221void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)222{223	dma_free_attrs(&adev->auxdev.dev, adev->pkg_dir_size, adev->pkg_dir,224		       adev->pkg_dir_dma_addr, 0);225}226EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);227 228static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,229				 unsigned long cpd_size,230				 unsigned long data_size)231{232	const struct ipu6_cpd_hdr *cpd_hdr = cpd;233	const struct ipu6_cpd_ent *ent;234	unsigned int i;235	u8 len;236 237	len = cpd_hdr->hdr_len;238 239	/* Ensure cpd hdr is within moduledata */240	if (cpd_size < len) {241		dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");242		return -EINVAL;243	}244 245	/* Sanity check for CPD header */246	if ((cpd_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {247		dev_err(&isp->pdev->dev, "Invalid CPD header\n");248		return -EINVAL;249	}250 251	/* Ensure that all entries are within moduledata */252	ent = (const struct ipu6_cpd_ent *)(((const u8 *)cpd_hdr) + len);253	for (i = 0; i < cpd_hdr->ent_cnt; i++, ent++) {254		if (data_size < ent->offset ||255		    data_size - ent->offset < ent->len) {256			dev_err(&isp->pdev->dev, "Invalid CPD entry (%d)\n", i);257			return -EINVAL;258		}259	}260 261	return 0;262}263 264static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,265					const void *moduledata,266					u32 moduledata_size)267{268	const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;269	int ret;270 271	/* Ensure moduledata hdr is within moduledata */272	if (moduledata_size < sizeof(*mod_hdr) ||273	    moduledata_size < mod_hdr->hdr_len) {274		dev_err(&isp->pdev->dev, "Invalid CPD moduledata size\n");275		return -EINVAL;276	}277 278	dev_info(&isp->pdev->dev, "FW version: %x\n", mod_hdr->fw_pkg_date);279	ret = ipu6_cpd_validate_cpd(isp, moduledata + mod_hdr->hdr_len,280				    moduledata_size - mod_hdr->hdr_len,281				    moduledata_size);282	if (ret) {283		dev_err(&isp->pdev->dev, "Invalid CPD in moduledata\n");284		return ret;285	}286 287	return 0;288}289 290static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,291				      const void *metadata, u32 meta_size)292{293	const struct ipu6_cpd_metadata_extn *extn = metadata;294 295	/* Sanity check for metadata size */296	if (meta_size < sizeof(*extn) || meta_size > MAX_METADATA_SIZE) {297		dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");298		return -EINVAL;299	}300 301	/* Validate extension and image types */302	if (extn->extn_type != IPU6_CPD_METADATA_EXTN_TYPE_IUNIT ||303	    extn->img_type != IPU6_CPD_METADATA_IMAGE_TYPE_MAIN_FIRMWARE) {304		dev_err(&isp->pdev->dev,305			"Invalid CPD metadata descriptor img_type (%d)\n",306			extn->img_type);307		return -EINVAL;308	}309 310	/* Validate metadata size multiple of metadata components */311	if ((meta_size - sizeof(*extn)) % isp->cpd_metadata_cmpnt_size) {312		dev_err(&isp->pdev->dev, "Invalid CPD metadata size\n");313		return -EINVAL;314	}315 316	return 0;317}318 319int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,320			       unsigned long cpd_file_size)321{322	const struct ipu6_cpd_hdr *hdr = cpd_file;323	const struct ipu6_cpd_ent *ent;324	int ret;325 326	ret = ipu6_cpd_validate_cpd(isp, cpd_file, cpd_file_size,327				    cpd_file_size);328	if (ret) {329		dev_err(&isp->pdev->dev, "Invalid CPD in file\n");330		return ret;331	}332 333	/* Check for CPD file marker */334	if (hdr->hdr_mark != CPD_HDR_MARK) {335		dev_err(&isp->pdev->dev, "Invalid CPD header\n");336		return -EINVAL;337	}338 339	/* Sanity check for manifest size */340	ent = ipu6_cpd_get_manifest(cpd_file);341	if (ent->len > MAX_MANIFEST_SIZE) {342		dev_err(&isp->pdev->dev, "Invalid CPD manifest size\n");343		return -EINVAL;344	}345 346	/* Validate metadata */347	ent = ipu6_cpd_get_metadata(cpd_file);348	ret = ipu6_cpd_validate_metadata(isp, cpd_file + ent->offset, ent->len);349	if (ret) {350		dev_err(&isp->pdev->dev, "Invalid CPD metadata\n");351		return ret;352	}353 354	/* Validate moduledata */355	ent = ipu6_cpd_get_moduledata(cpd_file);356	ret = ipu6_cpd_validate_moduledata(isp, cpd_file + ent->offset,357					   ent->len);358	if (ret)359		dev_err(&isp->pdev->dev, "Invalid CPD moduledata\n");360 361	return ret;362}363