File Index
Files and directories
Sign in
FILE

ipu7-cpd.c

File preview with syntax highlighting.

linux-6.19.14/drivers/staging/media/ipu7/ipu7-cpd.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2015 - 2025 Intel Corporation
  4 */
  5
  6#include <linux/device.h>
  7#include <linux/export.h>
  8#include <linux/gfp_types.h>
  9#include <linux/pci.h>
 10#include <linux/sizes.h>
 11#include <linux/slab.h>
 12#include <linux/types.h>
 13
 14#include "ipu7.h"
 15#include "ipu7-cpd.h"
 16
 17/* $CPD */
 18#define CPD_HDR_MARK		0x44504324
 19
 20/* Maximum size is 4K DWORDs or 16KB */
 21#define MAX_MANIFEST_SIZE	(SZ_4K * sizeof(u32))
 22
 23#define CPD_MANIFEST_IDX	0
 24#define CPD_BINARY_START_IDX	1U
 25#define CPD_METADATA_START_IDX	2U
 26#define CPD_BINARY_NUM		2U /* ISYS + PSYS */
 27/*
 28 * Entries include:
 29 * 1 manifest entry.
 30 * 1 metadata entry for each sub system(ISYS and PSYS).
 31 * 1 binary entry for each sub system(ISYS and PSYS).
 32 */
 33#define CPD_ENTRY_NUM		(CPD_BINARY_NUM * 2U + 1U)
 34
 35#define CPD_METADATA_ATTR	0xa
 36#define CPD_METADATA_IPL	0x1c
 37#define ONLINE_METADATA_SIZE	128U
 38#define ONLINE_METADATA_LINES	6U
 39
 40struct ipu7_cpd_hdr {
 41	u32 hdr_mark;
 42	u32 ent_cnt;
 43	u8 hdr_ver;
 44	u8 ent_ver;
 45	u8 hdr_len;
 46	u8 rsvd;
 47	u8 partition_name[4];
 48	u32 crc32;
 49} __packed;
 50
 51struct ipu7_cpd_ent {
 52	u8 name[12];
 53	u32 offset;
 54	u32 len;
 55	u8 rsvd[4];
 56} __packed;
 57
 58struct ipu7_cpd_metadata_hdr {
 59	u32 type;
 60	u32 len;
 61} __packed;
 62
 63struct ipu7_cpd_metadata_attr {
 64	struct ipu7_cpd_metadata_hdr hdr;
 65	u8 compression_type;
 66	u8 encryption_type;
 67	u8 rsvd[2];
 68	u32 uncompressed_size;
 69	u32 compressed_size;
 70	u32 module_id;
 71	u8 hash[48];
 72} __packed;
 73
 74struct ipu7_cpd_metadata_ipl {
 75	struct ipu7_cpd_metadata_hdr hdr;
 76	u32 param[4];
 77	u8 rsvd[8];
 78} __packed;
 79
 80struct ipu7_cpd_metadata {
 81	struct ipu7_cpd_metadata_attr attr;
 82	struct ipu7_cpd_metadata_ipl ipl;
 83} __packed;
 84
 85static inline struct ipu7_cpd_ent *ipu7_cpd_get_entry(const void *cpd, int idx)
 86{
 87	const struct ipu7_cpd_hdr *cpd_hdr = cpd;
 88
 89	return ((struct ipu7_cpd_ent *)((u8 *)cpd + cpd_hdr->hdr_len)) + idx;
 90}
 91
 92#define ipu7_cpd_get_manifest(cpd) ipu7_cpd_get_entry(cpd, 0)
 93
 94static struct ipu7_cpd_metadata *ipu7_cpd_get_metadata(const void *cpd, int idx)
 95{
 96	struct ipu7_cpd_ent *cpd_ent =
 97		ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2);
 98
 99	return (struct ipu7_cpd_metadata *)((u8 *)cpd + cpd_ent->offset);
100}
101
102static int ipu7_cpd_validate_cpd(struct ipu7_device *isp,
103				 const void *cpd, unsigned long data_size)
104{
105	const struct ipu7_cpd_hdr *cpd_hdr = cpd;
106	struct device *dev = &isp->pdev->dev;
107	struct ipu7_cpd_ent *ent;
108	unsigned int i;
109	u8 len;
110
111	len = cpd_hdr->hdr_len;
112
113	/* Ensure cpd hdr is within moduledata */
114	if (data_size < len) {
115		dev_err(dev, "Invalid CPD moduledata size\n");
116		return -EINVAL;
117	}
118
119	/* Check for CPD file marker */
120	if (cpd_hdr->hdr_mark != CPD_HDR_MARK) {
121		dev_err(dev, "Invalid CPD header marker\n");
122		return -EINVAL;
123	}
124
125	/* Sanity check for CPD entry header */
126	if (cpd_hdr->ent_cnt != CPD_ENTRY_NUM) {
127		dev_err(dev, "Invalid CPD entry number %d\n",
128			cpd_hdr->ent_cnt);
129		return -EINVAL;
130	}
131	if ((data_size - len) / sizeof(*ent) < cpd_hdr->ent_cnt) {
132		dev_err(dev, "Invalid CPD entry headers\n");
133		return -EINVAL;
134	}
135
136	/* Ensure that all entries are within moduledata */
137	ent = (struct ipu7_cpd_ent *)(((u8 *)cpd_hdr) + len);
138	for (i = 0; i < cpd_hdr->ent_cnt; i++) {
139		if (data_size < ent->offset ||
140		    data_size - ent->offset < ent->len) {
141			dev_err(dev, "Invalid CPD entry %d\n", i);
142			return -EINVAL;
143		}
144		ent++;
145	}
146
147	return 0;
148}
149
150static int ipu7_cpd_validate_metadata(struct ipu7_device *isp,
151				      const void *cpd, int idx)
152{
153	const struct ipu7_cpd_ent *cpd_ent =
154		ipu7_cpd_get_entry(cpd, CPD_METADATA_START_IDX + idx * 2);
155	const struct ipu7_cpd_metadata *metadata =
156		ipu7_cpd_get_metadata(cpd, idx);
157	struct device *dev = &isp->pdev->dev;
158
159	/* Sanity check for metadata size */
160	if (cpd_ent->len != sizeof(struct ipu7_cpd_metadata)) {
161		dev_err(dev, "Invalid metadata size\n");
162		return -EINVAL;
163	}
164
165	/* Validate type and length of metadata sections */
166	if (metadata->attr.hdr.type != CPD_METADATA_ATTR) {
167		dev_err(dev, "Invalid metadata attr type (%d)\n",
168			metadata->attr.hdr.type);
169		return -EINVAL;
170	}
171	if (metadata->attr.hdr.len != sizeof(struct ipu7_cpd_metadata_attr)) {
172		dev_err(dev, "Invalid metadata attr size (%d)\n",
173			metadata->attr.hdr.len);
174		return -EINVAL;
175	}
176	if (metadata->ipl.hdr.type != CPD_METADATA_IPL) {
177		dev_err(dev, "Invalid metadata ipl type (%d)\n",
178			metadata->ipl.hdr.type);
179		return -EINVAL;
180	}
181	if (metadata->ipl.hdr.len != sizeof(struct ipu7_cpd_metadata_ipl)) {
182		dev_err(dev, "Invalid metadata ipl size (%d)\n",
183			metadata->ipl.hdr.len);
184		return -EINVAL;
185	}
186
187	return 0;
188}
189
190int ipu7_cpd_validate_cpd_file(struct ipu7_device *isp, const void *cpd_file,
191			       unsigned long cpd_file_size)
192{
193	struct device *dev = &isp->pdev->dev;
194	struct ipu7_cpd_ent *ent;
195	unsigned int i;
196	int ret;
197	char *buf;
198
199	ret = ipu7_cpd_validate_cpd(isp, cpd_file, cpd_file_size);
200	if (ret) {
201		dev_err(dev, "Invalid CPD in file\n");
202		return -EINVAL;
203	}
204
205	/* Sanity check for manifest size */
206	ent = ipu7_cpd_get_manifest(cpd_file);
207	if (ent->len > MAX_MANIFEST_SIZE) {
208		dev_err(dev, "Invalid manifest size\n");
209		return -EINVAL;
210	}
211
212	/* Validate metadata */
213	for (i = 0; i < CPD_BINARY_NUM; i++) {
214		ret = ipu7_cpd_validate_metadata(isp, cpd_file, i);
215		if (ret) {
216			dev_err(dev, "Invalid metadata%d\n", i);
217			return ret;
218		}
219	}
220
221	/* Get fw binary version. */
222	buf = kmalloc(ONLINE_METADATA_SIZE, GFP_KERNEL);
223	if (!buf)
224		return -ENOMEM;
225	for (i = 0; i < CPD_BINARY_NUM; i++) {
226		char *lines[ONLINE_METADATA_LINES];
227		char *info = buf;
228		unsigned int l;
229
230		ent = ipu7_cpd_get_entry(cpd_file,
231					 CPD_BINARY_START_IDX + i * 2U);
232		memcpy(info, (u8 *)cpd_file + ent->offset + ent->len -
233		       ONLINE_METADATA_SIZE, ONLINE_METADATA_SIZE);
234		for (l = 0; l < ONLINE_METADATA_LINES; l++) {
235			lines[l] = strsep((char **)&info, "\n");
236			if (!lines[l])
237				break;
238		}
239		if (l < ONLINE_METADATA_LINES) {
240			dev_err(dev, "Failed to parse fw binary%d info.\n", i);
241			continue;
242		}
243		dev_info(dev, "FW binary%d info:\n", i);
244		dev_info(dev, "Name: %s\n", lines[1]);
245		dev_info(dev, "Version: %s\n", lines[2]);
246		dev_info(dev, "Timestamp: %s\n", lines[3]);
247		dev_info(dev, "Commit: %s\n", lines[4]);
248	}
249	kfree(buf);
250
251	return 0;
252}
253EXPORT_SYMBOL_NS_GPL(ipu7_cpd_validate_cpd_file, "INTEL_IPU7");
254
255int ipu7_cpd_copy_binary(const void *cpd, const char *name,
256			 void *code_region, u32 *entry)
257{
258	unsigned int i;
259
260	for (i = 0; i < CPD_BINARY_NUM; i++) {
261		const struct ipu7_cpd_ent *binary =
262			ipu7_cpd_get_entry(cpd, CPD_BINARY_START_IDX + i * 2U);
263		const struct ipu7_cpd_metadata *metadata =
264			ipu7_cpd_get_metadata(cpd, i);
265
266		if (!strncmp(binary->name, name, sizeof(binary->name))) {
267			memcpy(code_region + metadata->ipl.param[0],
268			       cpd + binary->offset, binary->len);
269			*entry = metadata->ipl.param[2];
270			return 0;
271		}
272	}
273
274	return -ENOENT;
275}
276EXPORT_SYMBOL_NS_GPL(ipu7_cpd_copy_binary, "INTEL_IPU7");
~/ Read-only file preview.