Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / media / pci / intel / ipu6 / ipu6-cpd.c
blob55ffd988ae4f1c1ab5b431ddb3593792971ce18d
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (C) 2013--2024 Intel Corporation
4 */
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>
15 #include "ipu6.h"
16 #include "ipu6-bus.h"
17 #include "ipu6-cpd.h"
18 #include "ipu6-dma.h"
20 /* 15 entries + header*/
21 #define MAX_PKG_DIR_ENT_CNT 16
22 /* 2 qword per entry/header */
23 #define PKG_DIR_ENT_LEN 2
24 /* PKG_DIR size in bytes */
25 #define PKG_DIR_SIZE ((MAX_PKG_DIR_ENT_CNT) * \
26 (PKG_DIR_ENT_LEN) * sizeof(u64))
27 /* _IUPKDR_ */
28 #define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL
30 /* $CPD */
31 #define CPD_HDR_MARK 0x44504324
33 #define MAX_MANIFEST_SIZE (SZ_2K * sizeof(u32))
34 #define MAX_METADATA_SIZE SZ_64K
36 #define MAX_COMPONENT_ID 127
37 #define MAX_COMPONENT_VERSION 0xffff
39 #define MANIFEST_IDX 0
40 #define METADATA_IDX 1
41 #define MODULEDATA_IDX 2
43 * PKG_DIR Entry (type == id)
44 * 63:56 55 54:48 47:32 31:24 23:0
45 * Rsvd Rsvd Type Version Rsvd Size
47 #define PKG_DIR_SIZE_MASK GENMASK_ULL(23, 0)
48 #define PKG_DIR_VERSION_MASK GENMASK_ULL(47, 32)
49 #define PKG_DIR_TYPE_MASK GENMASK_ULL(54, 48)
51 static inline const struct ipu6_cpd_ent *ipu6_cpd_get_entry(const void *cpd,
52 u8 idx)
54 const struct ipu6_cpd_hdr *cpd_hdr = cpd;
55 const struct ipu6_cpd_ent *ent;
57 ent = (const struct ipu6_cpd_ent *)((const u8 *)cpd + cpd_hdr->hdr_len);
58 return ent + idx;
61 #define ipu6_cpd_get_manifest(cpd) ipu6_cpd_get_entry(cpd, MANIFEST_IDX)
62 #define ipu6_cpd_get_metadata(cpd) ipu6_cpd_get_entry(cpd, METADATA_IDX)
63 #define ipu6_cpd_get_moduledata(cpd) ipu6_cpd_get_entry(cpd, MODULEDATA_IDX)
65 static const struct ipu6_cpd_metadata_cmpnt_hdr *
66 ipu6_cpd_metadata_get_cmpnt(struct ipu6_device *isp, const void *metadata,
67 unsigned int metadata_size, u8 idx)
69 size_t extn_size = sizeof(struct ipu6_cpd_metadata_extn);
70 size_t cmpnt_count = metadata_size - extn_size;
72 cmpnt_count = div_u64(cmpnt_count, isp->cpd_metadata_cmpnt_size);
74 if (idx > MAX_COMPONENT_ID || idx >= cmpnt_count) {
75 dev_err(&isp->pdev->dev, "Component index out of range (%d)\n",
76 idx);
77 return ERR_PTR(-EINVAL);
80 return metadata + extn_size + idx * isp->cpd_metadata_cmpnt_size;
83 static u32 ipu6_cpd_metadata_cmpnt_version(struct ipu6_device *isp,
84 const void *metadata,
85 unsigned int metadata_size, u8 idx)
87 const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
89 cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
90 if (IS_ERR(cmpnt))
91 return PTR_ERR(cmpnt);
93 return cmpnt->ver;
96 static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device *isp,
97 const void *metadata,
98 unsigned int metadata_size, u8 idx)
100 const struct ipu6_cpd_metadata_cmpnt_hdr *cmpnt;
102 cmpnt = ipu6_cpd_metadata_get_cmpnt(isp, metadata, metadata_size, idx);
103 if (IS_ERR(cmpnt))
104 return PTR_ERR(cmpnt);
106 return cmpnt->id;
109 static int ipu6_cpd_parse_module_data(struct ipu6_device *isp,
110 const void *module_data,
111 unsigned int module_data_size,
112 dma_addr_t dma_addr_module_data,
113 u64 *pkg_dir, const void *metadata,
114 unsigned int metadata_size)
116 const struct ipu6_cpd_module_data_hdr *module_data_hdr;
117 const struct ipu6_cpd_hdr *dir_hdr;
118 const struct ipu6_cpd_ent *dir_ent;
119 unsigned int i;
120 u8 len;
122 if (!module_data)
123 return -EINVAL;
125 module_data_hdr = module_data;
126 dir_hdr = module_data + module_data_hdr->hdr_len;
127 len = dir_hdr->hdr_len;
128 dir_ent = (const struct ipu6_cpd_ent *)(((u8 *)dir_hdr) + len);
130 pkg_dir[0] = PKG_DIR_HDR_MARK;
131 /* pkg_dir entry count = component count + pkg_dir header */
132 pkg_dir[1] = dir_hdr->ent_cnt + 1;
134 for (i = 0; i < dir_hdr->ent_cnt; i++, dir_ent++) {
135 u64 *p = &pkg_dir[PKG_DIR_ENT_LEN * (1 + i)];
136 int ver, id;
138 *p++ = dma_addr_module_data + dir_ent->offset;
139 id = ipu6_cpd_metadata_get_cmpnt_id(isp, metadata,
140 metadata_size, i);
141 if (id < 0 || id > MAX_COMPONENT_ID) {
142 dev_err(&isp->pdev->dev, "Invalid CPD component id\n");
143 return -EINVAL;
146 ver = ipu6_cpd_metadata_cmpnt_version(isp, metadata,
147 metadata_size, i);
148 if (ver < 0 || ver > MAX_COMPONENT_VERSION) {
149 dev_err(&isp->pdev->dev,
150 "Invalid CPD component version\n");
151 return -EINVAL;
154 *p = FIELD_PREP(PKG_DIR_SIZE_MASK, dir_ent->len) |
155 FIELD_PREP(PKG_DIR_TYPE_MASK, id) |
156 FIELD_PREP(PKG_DIR_VERSION_MASK, ver);
159 return 0;
162 int ipu6_cpd_create_pkg_dir(struct ipu6_bus_device *adev, const void *src)
164 dma_addr_t dma_addr_src = sg_dma_address(adev->fw_sgt.sgl);
165 const struct ipu6_cpd_ent *ent, *man_ent, *met_ent;
166 struct ipu6_device *isp = adev->isp;
167 unsigned int man_sz, met_sz;
168 void *pkg_dir_pos;
169 int ret;
171 man_ent = ipu6_cpd_get_manifest(src);
172 man_sz = man_ent->len;
174 met_ent = ipu6_cpd_get_metadata(src);
175 met_sz = met_ent->len;
177 adev->pkg_dir_size = PKG_DIR_SIZE + man_sz + met_sz;
178 adev->pkg_dir = ipu6_dma_alloc(adev, adev->pkg_dir_size,
179 &adev->pkg_dir_dma_addr, GFP_KERNEL, 0);
180 if (!adev->pkg_dir)
181 return -ENOMEM;
184 * pkg_dir entry/header:
185 * qword | 63:56 | 55 | 54:48 | 47:32 | 31:24 | 23:0
186 * N Address/Offset/"_IUPKDR_"
187 * N + 1 | rsvd | rsvd | type | ver | rsvd | size
189 * We can ignore other fields that size in N + 1 qword as they
190 * are 0 anyway. Just setting size for now.
193 ent = ipu6_cpd_get_moduledata(src);
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 ipu6_dma_free(adev, adev->pkg_dir_size,
202 adev->pkg_dir, adev->pkg_dir_dma_addr, 0);
203 return ret;
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);
210 /* Copy metadata after manifest */
211 pkg_dir_pos += man_sz;
212 memcpy(pkg_dir_pos, src + met_ent->offset, met_sz);
214 ipu6_dma_sync_single(adev, adev->pkg_dir_dma_addr,
215 adev->pkg_dir_size);
217 return 0;
219 EXPORT_SYMBOL_NS_GPL(ipu6_cpd_create_pkg_dir, INTEL_IPU6);
221 void ipu6_cpd_free_pkg_dir(struct ipu6_bus_device *adev)
223 ipu6_dma_free(adev, adev->pkg_dir_size, adev->pkg_dir,
224 adev->pkg_dir_dma_addr, 0);
226 EXPORT_SYMBOL_NS_GPL(ipu6_cpd_free_pkg_dir, INTEL_IPU6);
228 static int ipu6_cpd_validate_cpd(struct ipu6_device *isp, const void *cpd,
229 unsigned long cpd_size,
230 unsigned long data_size)
232 const struct ipu6_cpd_hdr *cpd_hdr = cpd;
233 const struct ipu6_cpd_ent *ent;
234 unsigned int i;
235 u8 len;
237 len = cpd_hdr->hdr_len;
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;
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;
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;
261 return 0;
264 static int ipu6_cpd_validate_moduledata(struct ipu6_device *isp,
265 const void *moduledata,
266 u32 moduledata_size)
268 const struct ipu6_cpd_module_data_hdr *mod_hdr = moduledata;
269 int ret;
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;
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;
287 return 0;
290 static int ipu6_cpd_validate_metadata(struct ipu6_device *isp,
291 const void *metadata, u32 meta_size)
293 const struct ipu6_cpd_metadata_extn *extn = metadata;
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;
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;
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;
316 return 0;
319 int ipu6_cpd_validate_cpd_file(struct ipu6_device *isp, const void *cpd_file,
320 unsigned long cpd_file_size)
322 const struct ipu6_cpd_hdr *hdr = cpd_file;
323 const struct ipu6_cpd_ent *ent;
324 int ret;
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;
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;
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;
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;
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");
361 return ret;