1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2013--2024 Intel Corporation
6 #include <linux/bitfield.h>
7 #include <linux/bits.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>
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))
28 #define PKG_DIR_HDR_MARK 0x5f4955504b44525fULL
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
,
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
);
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",
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
,
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
);
91 return PTR_ERR(cmpnt
);
96 static int ipu6_cpd_metadata_get_cmpnt_id(struct ipu6_device
*isp
,
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
);
104 return PTR_ERR(cmpnt
);
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
;
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
)];
138 *p
++ = dma_addr_module_data
+ dir_ent
->offset
;
139 id
= ipu6_cpd_metadata_get_cmpnt_id(isp
, metadata
,
141 if (id
< 0 || id
> MAX_COMPONENT_ID
) {
142 dev_err(&isp
->pdev
->dev
, "Invalid CPD component id\n");
146 ver
= ipu6_cpd_metadata_cmpnt_version(isp
, metadata
,
148 if (ver
< 0 || ver
> MAX_COMPONENT_VERSION
) {
149 dev_err(&isp
->pdev
->dev
,
150 "Invalid CPD component version\n");
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
);
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
;
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);
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
,
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);
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
,
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
;
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");
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");
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
);
264 static int ipu6_cpd_validate_moduledata(struct ipu6_device
*isp
,
265 const void *moduledata
,
268 const struct ipu6_cpd_module_data_hdr
*mod_hdr
= moduledata
;
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");
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
,
283 dev_err(&isp
->pdev
->dev
, "Invalid CPD in moduledata\n");
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");
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",
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");
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
;
326 ret
= ipu6_cpd_validate_cpd(isp
, cpd_file
, cpd_file_size
,
329 dev_err(&isp
->pdev
->dev
, "Invalid CPD in file\n");
333 /* Check for CPD file marker */
334 if (hdr
->hdr_mark
!= CPD_HDR_MARK
) {
335 dev_err(&isp
->pdev
->dev
, "Invalid CPD header\n");
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");
346 /* Validate metadata */
347 ent
= ipu6_cpd_get_metadata(cpd_file
);
348 ret
= ipu6_cpd_validate_metadata(isp
, cpd_file
+ ent
->offset
, ent
->len
);
350 dev_err(&isp
->pdev
->dev
, "Invalid CPD metadata\n");
354 /* Validate moduledata */
355 ent
= ipu6_cpd_get_moduledata(cpd_file
);
356 ret
= ipu6_cpd_validate_moduledata(isp
, cpd_file
+ ent
->offset
,
359 dev_err(&isp
->pdev
->dev
, "Invalid CPD moduledata\n");