1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* |
3 | * CCS static data binary parser library |
4 | * |
5 | * Copyright 2019--2020 Intel Corporation |
6 | */ |
7 | |
8 | #include <linux/device.h> |
9 | #include <linux/errno.h> |
10 | #include <linux/limits.h> |
11 | #include <linux/mm.h> |
12 | #include <linux/slab.h> |
13 | |
14 | #include "ccs-data-defs.h" |
15 | |
16 | struct bin_container { |
17 | void *base; |
18 | void *now; |
19 | void *end; |
20 | size_t size; |
21 | }; |
22 | |
23 | static void *bin_alloc(struct bin_container *bin, size_t len) |
24 | { |
25 | void *ptr; |
26 | |
27 | len = ALIGN(len, 8); |
28 | |
29 | if (bin->end - bin->now < len) |
30 | return NULL; |
31 | |
32 | ptr = bin->now; |
33 | bin->now += len; |
34 | |
35 | return ptr; |
36 | } |
37 | |
38 | static void bin_reserve(struct bin_container *bin, size_t len) |
39 | { |
40 | bin->size += ALIGN(len, 8); |
41 | } |
42 | |
43 | static int bin_backing_alloc(struct bin_container *bin) |
44 | { |
45 | bin->base = bin->now = kvzalloc(size: bin->size, GFP_KERNEL); |
46 | if (!bin->base) |
47 | return -ENOMEM; |
48 | |
49 | bin->end = bin->base + bin->size; |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | #define is_contained(var, endp) \ |
55 | (sizeof(*var) <= (endp) - (void *)(var)) |
56 | #define has_headroom(ptr, headroom, endp) \ |
57 | ((headroom) <= (endp) - (void *)(ptr)) |
58 | #define is_contained_with_headroom(var, headroom, endp) \ |
59 | (sizeof(*var) + (headroom) <= (endp) - (void *)(var)) |
60 | |
61 | static int |
62 | ccs_data_parse_length_specifier(const struct __ccs_data_length_specifier *__len, |
63 | size_t *__hlen, size_t *__plen, |
64 | const void *endp) |
65 | { |
66 | size_t hlen, plen; |
67 | |
68 | if (!is_contained(__len, endp)) |
69 | return -ENODATA; |
70 | |
71 | switch (__len->length >> CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) { |
72 | case CCS_DATA_LENGTH_SPECIFIER_1: |
73 | hlen = sizeof(*__len); |
74 | plen = __len->length & |
75 | ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1); |
76 | break; |
77 | case CCS_DATA_LENGTH_SPECIFIER_2: { |
78 | struct __ccs_data_length_specifier2 *__len2 = (void *)__len; |
79 | |
80 | if (!is_contained(__len2, endp)) |
81 | return -ENODATA; |
82 | |
83 | hlen = sizeof(*__len2); |
84 | plen = ((size_t) |
85 | (__len2->length[0] & |
86 | ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1)) |
87 | << 8) + __len2->length[1]; |
88 | break; |
89 | } |
90 | case CCS_DATA_LENGTH_SPECIFIER_3: { |
91 | struct __ccs_data_length_specifier3 *__len3 = (void *)__len; |
92 | |
93 | if (!is_contained(__len3, endp)) |
94 | return -ENODATA; |
95 | |
96 | hlen = sizeof(*__len3); |
97 | plen = ((size_t) |
98 | (__len3->length[0] & |
99 | ((1 << CCS_DATA_LENGTH_SPECIFIER_SIZE_SHIFT) - 1)) |
100 | << 16) + (__len3->length[0] << 8) + __len3->length[1]; |
101 | break; |
102 | } |
103 | default: |
104 | return -EINVAL; |
105 | } |
106 | |
107 | if (!has_headroom(__len, hlen + plen, endp)) |
108 | return -ENODATA; |
109 | |
110 | *__hlen = hlen; |
111 | *__plen = plen; |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | static u8 |
117 | ccs_data_parse_format_version(const struct __ccs_data_block *block) |
118 | { |
119 | return block->id >> CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT; |
120 | } |
121 | |
122 | static u8 ccs_data_parse_block_id(const struct __ccs_data_block *block, |
123 | bool is_first) |
124 | { |
125 | if (!is_first) |
126 | return block->id; |
127 | |
128 | return block->id & ((1 << CCS_DATA_BLOCK_HEADER_ID_VERSION_SHIFT) - 1); |
129 | } |
130 | |
131 | static int ccs_data_parse_version(struct bin_container *bin, |
132 | struct ccs_data_container *ccsdata, |
133 | const void *payload, const void *endp) |
134 | { |
135 | const struct __ccs_data_block_version *v = payload; |
136 | struct ccs_data_block_version *vv; |
137 | |
138 | if (v + 1 != endp) |
139 | return -ENODATA; |
140 | |
141 | if (!bin->base) { |
142 | bin_reserve(bin, len: sizeof(*ccsdata->version)); |
143 | return 0; |
144 | } |
145 | |
146 | ccsdata->version = bin_alloc(bin, len: sizeof(*ccsdata->version)); |
147 | if (!ccsdata->version) |
148 | return -ENOMEM; |
149 | |
150 | vv = ccsdata->version; |
151 | vv->version_major = ((u16)v->static_data_version_major[0] << 8) + |
152 | v->static_data_version_major[1]; |
153 | vv->version_minor = ((u16)v->static_data_version_minor[0] << 8) + |
154 | v->static_data_version_minor[1]; |
155 | vv->date_year = ((u16)v->year[0] << 8) + v->year[1]; |
156 | vv->date_month = v->month; |
157 | vv->date_day = v->day; |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | static void print_ccs_data_version(struct device *dev, |
163 | struct ccs_data_block_version *v) |
164 | { |
165 | dev_dbg(dev, |
166 | "static data version %4.4x.%4.4x, date %4.4u-%2.2u-%2.2u\n" , |
167 | v->version_major, v->version_minor, |
168 | v->date_year, v->date_month, v->date_day); |
169 | } |
170 | |
171 | static int (const struct __ccs_data_block *block, |
172 | bool is_first, unsigned int *__block_id, |
173 | const void **payload, |
174 | const struct __ccs_data_block **next_block, |
175 | const void *endp, struct device *dev, |
176 | bool verbose) |
177 | { |
178 | size_t plen, hlen; |
179 | u8 block_id; |
180 | int rval; |
181 | |
182 | if (!is_contained(block, endp)) |
183 | return -ENODATA; |
184 | |
185 | rval = ccs_data_parse_length_specifier(len: &block->length, hlen: &hlen, plen: &plen, |
186 | endp); |
187 | if (rval < 0) |
188 | return rval; |
189 | |
190 | block_id = ccs_data_parse_block_id(block, is_first); |
191 | |
192 | if (verbose) |
193 | dev_dbg(dev, |
194 | "Block ID 0x%2.2x, header length %zu, payload length %zu\n" , |
195 | block_id, hlen, plen); |
196 | |
197 | if (!has_headroom(&block->length, hlen + plen, endp)) |
198 | return -ENODATA; |
199 | |
200 | if (__block_id) |
201 | *__block_id = block_id; |
202 | |
203 | if (payload) |
204 | *payload = (void *)&block->length + hlen; |
205 | |
206 | if (next_block) |
207 | *next_block = (void *)&block->length + hlen + plen; |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static int ccs_data_parse_regs(struct bin_container *bin, |
213 | struct ccs_reg **__regs, |
214 | size_t *__num_regs, const void *payload, |
215 | const void *endp, struct device *dev) |
216 | { |
217 | struct ccs_reg *regs_base = NULL, *regs = NULL; |
218 | size_t num_regs = 0; |
219 | u16 addr = 0; |
220 | |
221 | if (bin->base && __regs) { |
222 | regs = regs_base = bin_alloc(bin, len: sizeof(*regs) * *__num_regs); |
223 | if (!regs) |
224 | return -ENOMEM; |
225 | } |
226 | |
227 | while (payload < endp && num_regs < INT_MAX) { |
228 | const struct __ccs_data_block_regs *r = payload; |
229 | size_t len; |
230 | const void *data; |
231 | |
232 | if (!is_contained(r, endp)) |
233 | return -ENODATA; |
234 | |
235 | switch (r->reg_len >> CCS_DATA_BLOCK_REGS_SEL_SHIFT) { |
236 | case CCS_DATA_BLOCK_REGS_SEL_REGS: |
237 | addr += r->reg_len & CCS_DATA_BLOCK_REGS_ADDR_MASK; |
238 | len = ((r->reg_len & CCS_DATA_BLOCK_REGS_LEN_MASK) |
239 | >> CCS_DATA_BLOCK_REGS_LEN_SHIFT) + 1; |
240 | |
241 | if (!is_contained_with_headroom(r, len, endp)) |
242 | return -ENODATA; |
243 | |
244 | data = r + 1; |
245 | break; |
246 | case CCS_DATA_BLOCK_REGS_SEL_REGS2: { |
247 | const struct __ccs_data_block_regs2 *r2 = payload; |
248 | |
249 | if (!is_contained(r2, endp)) |
250 | return -ENODATA; |
251 | |
252 | addr += ((u16)(r2->reg_len & |
253 | CCS_DATA_BLOCK_REGS_2_ADDR_MASK) << 8) |
254 | + r2->addr; |
255 | len = ((r2->reg_len & CCS_DATA_BLOCK_REGS_2_LEN_MASK) |
256 | >> CCS_DATA_BLOCK_REGS_2_LEN_SHIFT) + 1; |
257 | |
258 | if (!is_contained_with_headroom(r2, len, endp)) |
259 | return -ENODATA; |
260 | |
261 | data = r2 + 1; |
262 | break; |
263 | } |
264 | case CCS_DATA_BLOCK_REGS_SEL_REGS3: { |
265 | const struct __ccs_data_block_regs3 *r3 = payload; |
266 | |
267 | if (!is_contained(r3, endp)) |
268 | return -ENODATA; |
269 | |
270 | addr = ((u16)r3->addr[0] << 8) + r3->addr[1]; |
271 | len = (r3->reg_len & CCS_DATA_BLOCK_REGS_3_LEN_MASK) + 1; |
272 | |
273 | if (!is_contained_with_headroom(r3, len, endp)) |
274 | return -ENODATA; |
275 | |
276 | data = r3 + 1; |
277 | break; |
278 | } |
279 | default: |
280 | return -EINVAL; |
281 | } |
282 | |
283 | num_regs++; |
284 | |
285 | if (!bin->base) { |
286 | bin_reserve(bin, len); |
287 | } else if (__regs) { |
288 | if (!regs) |
289 | return -EIO; |
290 | |
291 | regs->addr = addr; |
292 | regs->len = len; |
293 | regs->value = bin_alloc(bin, len); |
294 | if (!regs->value) |
295 | return -ENOMEM; |
296 | |
297 | memcpy(regs->value, data, len); |
298 | regs++; |
299 | } |
300 | |
301 | addr += len; |
302 | payload = data + len; |
303 | } |
304 | |
305 | if (!bin->base) |
306 | bin_reserve(bin, len: sizeof(*regs) * num_regs); |
307 | |
308 | if (__num_regs) |
309 | *__num_regs = num_regs; |
310 | |
311 | if (bin->base && __regs) { |
312 | if (!regs_base) |
313 | return -EIO; |
314 | |
315 | *__regs = regs_base; |
316 | } |
317 | |
318 | return 0; |
319 | } |
320 | |
321 | static int ccs_data_parse_reg_rules(struct bin_container *bin, |
322 | struct ccs_reg **__regs, |
323 | size_t *__num_regs, |
324 | const void *payload, |
325 | const void *endp, struct device *dev) |
326 | { |
327 | int rval; |
328 | |
329 | if (!bin->base) |
330 | return ccs_data_parse_regs(bin, NULL, NULL, payload, endp, dev); |
331 | |
332 | rval = ccs_data_parse_regs(bin, NULL, __num_regs, payload, endp, dev); |
333 | if (rval) |
334 | return rval; |
335 | |
336 | return ccs_data_parse_regs(bin, __regs, __num_regs, payload, endp, |
337 | dev); |
338 | } |
339 | |
340 | static void assign_ffd_entry(struct ccs_frame_format_desc *desc, |
341 | const struct __ccs_data_block_ffd_entry *ent) |
342 | { |
343 | desc->pixelcode = ent->pixelcode; |
344 | desc->value = ((u16)ent->value[0] << 8) + ent->value[1]; |
345 | } |
346 | |
347 | static int ccs_data_parse_ffd(struct bin_container *bin, |
348 | struct ccs_frame_format_descs **ffd, |
349 | const void *payload, |
350 | const void *endp, struct device *dev) |
351 | { |
352 | const struct __ccs_data_block_ffd *__ffd = payload; |
353 | const struct __ccs_data_block_ffd_entry *__entry; |
354 | unsigned int i; |
355 | |
356 | if (!is_contained(__ffd, endp)) |
357 | return -ENODATA; |
358 | |
359 | if ((void *)__ffd + sizeof(*__ffd) + |
360 | ((u32)__ffd->num_column_descs + |
361 | (u32)__ffd->num_row_descs) * |
362 | sizeof(struct __ccs_data_block_ffd_entry) != endp) |
363 | return -ENODATA; |
364 | |
365 | if (!bin->base) { |
366 | bin_reserve(bin, len: sizeof(**ffd)); |
367 | bin_reserve(bin, len: __ffd->num_column_descs * |
368 | sizeof(struct ccs_frame_format_desc)); |
369 | bin_reserve(bin, len: __ffd->num_row_descs * |
370 | sizeof(struct ccs_frame_format_desc)); |
371 | |
372 | return 0; |
373 | } |
374 | |
375 | *ffd = bin_alloc(bin, len: sizeof(**ffd)); |
376 | if (!*ffd) |
377 | return -ENOMEM; |
378 | |
379 | (*ffd)->num_column_descs = __ffd->num_column_descs; |
380 | (*ffd)->num_row_descs = __ffd->num_row_descs; |
381 | __entry = (void *)(__ffd + 1); |
382 | |
383 | (*ffd)->column_descs = bin_alloc(bin, len: __ffd->num_column_descs * |
384 | sizeof(*(*ffd)->column_descs)); |
385 | if (!(*ffd)->column_descs) |
386 | return -ENOMEM; |
387 | |
388 | for (i = 0; i < __ffd->num_column_descs; i++, __entry++) |
389 | assign_ffd_entry(desc: &(*ffd)->column_descs[i], ent: __entry); |
390 | |
391 | (*ffd)->row_descs = bin_alloc(bin, len: __ffd->num_row_descs * |
392 | sizeof(*(*ffd)->row_descs)); |
393 | if (!(*ffd)->row_descs) |
394 | return -ENOMEM; |
395 | |
396 | for (i = 0; i < __ffd->num_row_descs; i++, __entry++) |
397 | assign_ffd_entry(desc: &(*ffd)->row_descs[i], ent: __entry); |
398 | |
399 | if (__entry != endp) |
400 | return -EPROTO; |
401 | |
402 | return 0; |
403 | } |
404 | |
405 | static int ccs_data_parse_pdaf_readout(struct bin_container *bin, |
406 | struct ccs_pdaf_readout **pdaf_readout, |
407 | const void *payload, |
408 | const void *endp, struct device *dev) |
409 | { |
410 | const struct __ccs_data_block_pdaf_readout *__pdaf = payload; |
411 | |
412 | if (!is_contained(__pdaf, endp)) |
413 | return -ENODATA; |
414 | |
415 | if (!bin->base) { |
416 | bin_reserve(bin, len: sizeof(**pdaf_readout)); |
417 | } else { |
418 | *pdaf_readout = bin_alloc(bin, len: sizeof(**pdaf_readout)); |
419 | if (!*pdaf_readout) |
420 | return -ENOMEM; |
421 | |
422 | (*pdaf_readout)->pdaf_readout_info_order = |
423 | __pdaf->pdaf_readout_info_order; |
424 | } |
425 | |
426 | return ccs_data_parse_ffd(bin, ffd: !bin->base ? NULL : &(*pdaf_readout)->ffd, |
427 | payload: __pdaf + 1, endp, dev); |
428 | } |
429 | |
430 | static int ccs_data_parse_rules(struct bin_container *bin, |
431 | struct ccs_rule **__rules, |
432 | size_t *__num_rules, const void *payload, |
433 | const void *endp, struct device *dev) |
434 | { |
435 | struct ccs_rule *rules_base = NULL, *rules = NULL, *next_rule = NULL; |
436 | size_t num_rules = 0; |
437 | const void *__next_rule = payload; |
438 | int rval; |
439 | |
440 | if (bin->base) { |
441 | rules_base = next_rule = |
442 | bin_alloc(bin, len: sizeof(*rules) * *__num_rules); |
443 | if (!rules_base) |
444 | return -ENOMEM; |
445 | } |
446 | |
447 | while (__next_rule < endp) { |
448 | size_t rule_hlen, rule_plen, rule_plen2; |
449 | const u8 *__rule_type; |
450 | const void *rule_payload; |
451 | |
452 | /* Size of a single rule */ |
453 | rval = ccs_data_parse_length_specifier(len: __next_rule, hlen: &rule_hlen, |
454 | plen: &rule_plen, endp); |
455 | |
456 | if (rval < 0) |
457 | return rval; |
458 | |
459 | __rule_type = __next_rule + rule_hlen; |
460 | |
461 | if (!is_contained(__rule_type, endp)) |
462 | return -ENODATA; |
463 | |
464 | rule_payload = __rule_type + 1; |
465 | rule_plen2 = rule_plen - sizeof(*__rule_type); |
466 | |
467 | if (*__rule_type == CCS_DATA_BLOCK_RULE_ID_IF) { |
468 | const struct __ccs_data_block_rule_if *__if_rules = |
469 | rule_payload; |
470 | const size_t __num_if_rules = |
471 | rule_plen2 / sizeof(*__if_rules); |
472 | struct ccs_if_rule *if_rule; |
473 | |
474 | if (!has_headroom(__if_rules, |
475 | sizeof(*__if_rules) * __num_if_rules, |
476 | rule_payload + rule_plen2)) |
477 | return -ENODATA; |
478 | |
479 | /* Also check there is no extra data */ |
480 | if (__if_rules + __num_if_rules != |
481 | rule_payload + rule_plen2) |
482 | return -EINVAL; |
483 | |
484 | if (!bin->base) { |
485 | bin_reserve(bin, |
486 | len: sizeof(*if_rule) * |
487 | __num_if_rules); |
488 | num_rules++; |
489 | } else { |
490 | unsigned int i; |
491 | |
492 | if (!next_rule) |
493 | return -EIO; |
494 | |
495 | rules = next_rule; |
496 | next_rule++; |
497 | |
498 | if_rule = bin_alloc(bin, |
499 | len: sizeof(*if_rule) * |
500 | __num_if_rules); |
501 | if (!if_rule) |
502 | return -ENOMEM; |
503 | |
504 | for (i = 0; i < __num_if_rules; i++) { |
505 | if_rule[i].addr = |
506 | ((u16)__if_rules[i].addr[0] |
507 | << 8) + |
508 | __if_rules[i].addr[1]; |
509 | if_rule[i].value = __if_rules[i].value; |
510 | if_rule[i].mask = __if_rules[i].mask; |
511 | } |
512 | |
513 | rules->if_rules = if_rule; |
514 | rules->num_if_rules = __num_if_rules; |
515 | } |
516 | } else { |
517 | /* Check there was an if rule before any other rules */ |
518 | if (bin->base && !rules) |
519 | return -EINVAL; |
520 | |
521 | switch (*__rule_type) { |
522 | case CCS_DATA_BLOCK_RULE_ID_READ_ONLY_REGS: |
523 | rval = ccs_data_parse_reg_rules(bin, |
524 | regs: rules ? |
525 | &rules->read_only_regs : NULL, |
526 | num_regs: rules ? |
527 | &rules->num_read_only_regs : NULL, |
528 | payload: rule_payload, |
529 | endp: rule_payload + rule_plen2, |
530 | dev); |
531 | if (rval) |
532 | return rval; |
533 | break; |
534 | case CCS_DATA_BLOCK_RULE_ID_FFD: |
535 | rval = ccs_data_parse_ffd(bin, ffd: rules ? |
536 | &rules->frame_format : NULL, |
537 | payload: rule_payload, |
538 | endp: rule_payload + rule_plen2, |
539 | dev); |
540 | if (rval) |
541 | return rval; |
542 | break; |
543 | case CCS_DATA_BLOCK_RULE_ID_MSR: |
544 | rval = ccs_data_parse_reg_rules(bin, |
545 | regs: rules ? |
546 | &rules->manufacturer_regs : NULL, |
547 | num_regs: rules ? |
548 | &rules->num_manufacturer_regs : NULL, |
549 | payload: rule_payload, |
550 | endp: rule_payload + rule_plen2, |
551 | dev); |
552 | if (rval) |
553 | return rval; |
554 | break; |
555 | case CCS_DATA_BLOCK_RULE_ID_PDAF_READOUT: |
556 | rval = ccs_data_parse_pdaf_readout(bin, |
557 | pdaf_readout: rules ? |
558 | &rules->pdaf_readout : NULL, |
559 | payload: rule_payload, |
560 | endp: rule_payload + rule_plen2, |
561 | dev); |
562 | if (rval) |
563 | return rval; |
564 | break; |
565 | default: |
566 | dev_dbg(dev, |
567 | "Don't know how to handle rule type %u!\n" , |
568 | *__rule_type); |
569 | return -EINVAL; |
570 | } |
571 | } |
572 | __next_rule = __next_rule + rule_hlen + rule_plen; |
573 | } |
574 | |
575 | if (!bin->base) { |
576 | bin_reserve(bin, len: sizeof(*rules) * num_rules); |
577 | *__num_rules = num_rules; |
578 | } else { |
579 | if (!rules_base) |
580 | return -EIO; |
581 | |
582 | *__rules = rules_base; |
583 | } |
584 | |
585 | return 0; |
586 | } |
587 | |
588 | static int ccs_data_parse_pdaf(struct bin_container *bin, struct ccs_pdaf_pix_loc **pdaf, |
589 | const void *payload, const void *endp, |
590 | struct device *dev) |
591 | { |
592 | const struct __ccs_data_block_pdaf_pix_loc *__pdaf = payload; |
593 | const struct __ccs_data_block_pdaf_pix_loc_block_desc_group *__bdesc_group; |
594 | const struct __ccs_data_block_pdaf_pix_loc_pixel_desc *__pixel_desc; |
595 | unsigned int i; |
596 | u16 num_block_desc_groups; |
597 | u8 max_block_type_id = 0; |
598 | const u8 *__num_pixel_descs; |
599 | |
600 | if (!is_contained(__pdaf, endp)) |
601 | return -ENODATA; |
602 | |
603 | if (bin->base) { |
604 | *pdaf = bin_alloc(bin, len: sizeof(**pdaf)); |
605 | if (!*pdaf) |
606 | return -ENOMEM; |
607 | } else { |
608 | bin_reserve(bin, len: sizeof(**pdaf)); |
609 | } |
610 | |
611 | num_block_desc_groups = |
612 | ((u16)__pdaf->num_block_desc_groups[0] << 8) + |
613 | __pdaf->num_block_desc_groups[1]; |
614 | |
615 | if (bin->base) { |
616 | (*pdaf)->main_offset_x = |
617 | ((u16)__pdaf->main_offset_x[0] << 8) + |
618 | __pdaf->main_offset_x[1]; |
619 | (*pdaf)->main_offset_y = |
620 | ((u16)__pdaf->main_offset_y[0] << 8) + |
621 | __pdaf->main_offset_y[1]; |
622 | (*pdaf)->global_pdaf_type = __pdaf->global_pdaf_type; |
623 | (*pdaf)->block_width = __pdaf->block_width; |
624 | (*pdaf)->block_height = __pdaf->block_height; |
625 | (*pdaf)->num_block_desc_groups = num_block_desc_groups; |
626 | } |
627 | |
628 | __bdesc_group = (const void *)(__pdaf + 1); |
629 | |
630 | if (bin->base) { |
631 | (*pdaf)->block_desc_groups = |
632 | bin_alloc(bin, |
633 | len: sizeof(struct ccs_pdaf_pix_loc_block_desc_group) * |
634 | num_block_desc_groups); |
635 | if (!(*pdaf)->block_desc_groups) |
636 | return -ENOMEM; |
637 | } else { |
638 | bin_reserve(bin, len: sizeof(struct ccs_pdaf_pix_loc_block_desc_group) * |
639 | num_block_desc_groups); |
640 | } |
641 | |
642 | for (i = 0; i < num_block_desc_groups; i++) { |
643 | const struct __ccs_data_block_pdaf_pix_loc_block_desc *__bdesc; |
644 | u16 num_block_descs; |
645 | unsigned int j; |
646 | |
647 | if (!is_contained(__bdesc_group, endp)) |
648 | return -ENODATA; |
649 | |
650 | num_block_descs = |
651 | ((u16)__bdesc_group->num_block_descs[0] << 8) + |
652 | __bdesc_group->num_block_descs[1]; |
653 | |
654 | if (bin->base) { |
655 | (*pdaf)->block_desc_groups[i].repeat_y = |
656 | __bdesc_group->repeat_y; |
657 | (*pdaf)->block_desc_groups[i].num_block_descs = |
658 | num_block_descs; |
659 | } |
660 | |
661 | __bdesc = (const void *)(__bdesc_group + 1); |
662 | |
663 | if (bin->base) { |
664 | (*pdaf)->block_desc_groups[i].block_descs = |
665 | bin_alloc(bin, |
666 | len: sizeof(struct ccs_pdaf_pix_loc_block_desc) * |
667 | num_block_descs); |
668 | if (!(*pdaf)->block_desc_groups[i].block_descs) |
669 | return -ENOMEM; |
670 | } else { |
671 | bin_reserve(bin, len: sizeof(struct ccs_pdaf_pix_loc_block_desc) * |
672 | num_block_descs); |
673 | } |
674 | |
675 | for (j = 0; j < num_block_descs; j++, __bdesc++) { |
676 | struct ccs_pdaf_pix_loc_block_desc *bdesc; |
677 | |
678 | if (!is_contained(__bdesc, endp)) |
679 | return -ENODATA; |
680 | |
681 | if (max_block_type_id <= __bdesc->block_type_id) |
682 | max_block_type_id = __bdesc->block_type_id + 1; |
683 | |
684 | if (!bin->base) |
685 | continue; |
686 | |
687 | bdesc = &(*pdaf)->block_desc_groups[i].block_descs[j]; |
688 | |
689 | bdesc->repeat_x = ((u16)__bdesc->repeat_x[0] << 8) |
690 | + __bdesc->repeat_x[1]; |
691 | |
692 | if (__bdesc->block_type_id >= num_block_descs) |
693 | return -EINVAL; |
694 | |
695 | bdesc->block_type_id = __bdesc->block_type_id; |
696 | } |
697 | |
698 | __bdesc_group = (const void *)__bdesc; |
699 | } |
700 | |
701 | __num_pixel_descs = (const void *)__bdesc_group; |
702 | |
703 | if (bin->base) { |
704 | (*pdaf)->pixel_desc_groups = |
705 | bin_alloc(bin, |
706 | len: sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) * |
707 | max_block_type_id); |
708 | if (!(*pdaf)->pixel_desc_groups) |
709 | return -ENOMEM; |
710 | (*pdaf)->num_pixel_desc_grups = max_block_type_id; |
711 | } else { |
712 | bin_reserve(bin, len: sizeof(struct ccs_pdaf_pix_loc_pixel_desc_group) * |
713 | max_block_type_id); |
714 | } |
715 | |
716 | for (i = 0; i < max_block_type_id; i++) { |
717 | struct ccs_pdaf_pix_loc_pixel_desc_group *pdgroup = NULL; |
718 | unsigned int j; |
719 | |
720 | if (!is_contained(__num_pixel_descs, endp)) |
721 | return -ENODATA; |
722 | |
723 | if (bin->base) { |
724 | pdgroup = &(*pdaf)->pixel_desc_groups[i]; |
725 | pdgroup->descs = |
726 | bin_alloc(bin, |
727 | len: sizeof(struct ccs_pdaf_pix_loc_pixel_desc) * |
728 | *__num_pixel_descs); |
729 | if (!pdgroup->descs) |
730 | return -ENOMEM; |
731 | pdgroup->num_descs = *__num_pixel_descs; |
732 | } else { |
733 | bin_reserve(bin, len: sizeof(struct ccs_pdaf_pix_loc_pixel_desc) * |
734 | *__num_pixel_descs); |
735 | } |
736 | |
737 | __pixel_desc = (const void *)(__num_pixel_descs + 1); |
738 | |
739 | for (j = 0; j < *__num_pixel_descs; j++, __pixel_desc++) { |
740 | struct ccs_pdaf_pix_loc_pixel_desc *pdesc; |
741 | |
742 | if (!is_contained(__pixel_desc, endp)) |
743 | return -ENODATA; |
744 | |
745 | if (!bin->base) |
746 | continue; |
747 | |
748 | if (!pdgroup) |
749 | return -EIO; |
750 | |
751 | pdesc = &pdgroup->descs[j]; |
752 | pdesc->pixel_type = __pixel_desc->pixel_type; |
753 | pdesc->small_offset_x = __pixel_desc->small_offset_x; |
754 | pdesc->small_offset_y = __pixel_desc->small_offset_y; |
755 | } |
756 | |
757 | __num_pixel_descs = (const void *)(__pixel_desc + 1); |
758 | } |
759 | |
760 | return 0; |
761 | } |
762 | |
763 | static int ccs_data_parse_license(struct bin_container *bin, |
764 | char **__license, |
765 | size_t *__license_length, |
766 | const void *payload, const void *endp) |
767 | { |
768 | size_t size = endp - payload; |
769 | char *license; |
770 | |
771 | if (!bin->base) { |
772 | bin_reserve(bin, len: size); |
773 | return 0; |
774 | } |
775 | |
776 | license = bin_alloc(bin, len: size); |
777 | if (!license) |
778 | return -ENOMEM; |
779 | |
780 | memcpy(license, payload, size); |
781 | |
782 | *__license = license; |
783 | *__license_length = size; |
784 | |
785 | return 0; |
786 | } |
787 | |
788 | static int ccs_data_parse_end(bool *end, const void *payload, const void *endp, |
789 | struct device *dev) |
790 | { |
791 | const struct __ccs_data_block_end *__end = payload; |
792 | |
793 | if (__end + 1 != endp) { |
794 | dev_dbg(dev, "Invalid end block length %u\n" , |
795 | (unsigned int)(endp - payload)); |
796 | return -ENODATA; |
797 | } |
798 | |
799 | *end = true; |
800 | |
801 | return 0; |
802 | } |
803 | |
804 | static int __ccs_data_parse(struct bin_container *bin, |
805 | struct ccs_data_container *ccsdata, |
806 | const void *data, size_t len, struct device *dev, |
807 | bool verbose) |
808 | { |
809 | const struct __ccs_data_block *block = data; |
810 | const struct __ccs_data_block *endp = data + len; |
811 | unsigned int version; |
812 | bool is_first = true; |
813 | int rval; |
814 | |
815 | version = ccs_data_parse_format_version(block); |
816 | if (version != CCS_STATIC_DATA_VERSION) { |
817 | dev_dbg(dev, "Don't know how to handle version %u\n" , version); |
818 | return -EINVAL; |
819 | } |
820 | |
821 | if (verbose) |
822 | dev_dbg(dev, "Parsing CCS static data version %u\n" , version); |
823 | |
824 | if (!bin->base) |
825 | *ccsdata = (struct ccs_data_container){ 0 }; |
826 | |
827 | while (block < endp) { |
828 | const struct __ccs_data_block *next_block; |
829 | unsigned int block_id; |
830 | const void *payload; |
831 | |
832 | rval = ccs_data_block_parse_header(block, is_first, &block_id, |
833 | &payload, &next_block, endp, |
834 | dev, |
835 | bin->base ? false : verbose); |
836 | |
837 | if (rval < 0) |
838 | return rval; |
839 | |
840 | switch (block_id) { |
841 | case CCS_DATA_BLOCK_ID_DUMMY: |
842 | break; |
843 | case CCS_DATA_BLOCK_ID_DATA_VERSION: |
844 | rval = ccs_data_parse_version(bin, ccsdata, payload, |
845 | next_block); |
846 | if (rval < 0) |
847 | return rval; |
848 | break; |
849 | case CCS_DATA_BLOCK_ID_SENSOR_READ_ONLY_REGS: |
850 | rval = ccs_data_parse_regs( |
851 | bin, &ccsdata->sensor_read_only_regs, |
852 | &ccsdata->num_sensor_read_only_regs, payload, |
853 | next_block, dev); |
854 | if (rval < 0) |
855 | return rval; |
856 | break; |
857 | case CCS_DATA_BLOCK_ID_SENSOR_MANUFACTURER_REGS: |
858 | rval = ccs_data_parse_regs( |
859 | bin, &ccsdata->sensor_manufacturer_regs, |
860 | &ccsdata->num_sensor_manufacturer_regs, payload, |
861 | next_block, dev); |
862 | if (rval < 0) |
863 | return rval; |
864 | break; |
865 | case CCS_DATA_BLOCK_ID_MODULE_READ_ONLY_REGS: |
866 | rval = ccs_data_parse_regs( |
867 | bin, &ccsdata->module_read_only_regs, |
868 | &ccsdata->num_module_read_only_regs, payload, |
869 | next_block, dev); |
870 | if (rval < 0) |
871 | return rval; |
872 | break; |
873 | case CCS_DATA_BLOCK_ID_MODULE_MANUFACTURER_REGS: |
874 | rval = ccs_data_parse_regs( |
875 | bin, &ccsdata->module_manufacturer_regs, |
876 | &ccsdata->num_module_manufacturer_regs, payload, |
877 | next_block, dev); |
878 | if (rval < 0) |
879 | return rval; |
880 | break; |
881 | case CCS_DATA_BLOCK_ID_SENSOR_PDAF_PIXEL_LOCATION: |
882 | rval = ccs_data_parse_pdaf(bin, &ccsdata->sensor_pdaf, |
883 | payload, next_block, dev); |
884 | if (rval < 0) |
885 | return rval; |
886 | break; |
887 | case CCS_DATA_BLOCK_ID_MODULE_PDAF_PIXEL_LOCATION: |
888 | rval = ccs_data_parse_pdaf(bin, &ccsdata->module_pdaf, |
889 | payload, next_block, dev); |
890 | if (rval < 0) |
891 | return rval; |
892 | break; |
893 | case CCS_DATA_BLOCK_ID_SENSOR_RULE_BASED_BLOCK: |
894 | rval = ccs_data_parse_rules( |
895 | bin, &ccsdata->sensor_rules, |
896 | &ccsdata->num_sensor_rules, payload, next_block, |
897 | dev); |
898 | if (rval < 0) |
899 | return rval; |
900 | break; |
901 | case CCS_DATA_BLOCK_ID_MODULE_RULE_BASED_BLOCK: |
902 | rval = ccs_data_parse_rules( |
903 | bin, &ccsdata->module_rules, |
904 | &ccsdata->num_module_rules, payload, next_block, |
905 | dev); |
906 | if (rval < 0) |
907 | return rval; |
908 | break; |
909 | case CCS_DATA_BLOCK_ID_LICENSE: |
910 | rval = ccs_data_parse_license(bin, &ccsdata->license, |
911 | &ccsdata->license_length, |
912 | payload, next_block); |
913 | if (rval < 0) |
914 | return rval; |
915 | break; |
916 | case CCS_DATA_BLOCK_ID_END: |
917 | rval = ccs_data_parse_end(&ccsdata->end, payload, |
918 | next_block, dev); |
919 | if (rval < 0) |
920 | return rval; |
921 | break; |
922 | default: |
923 | dev_dbg(dev, "WARNING: not handling block ID 0x%2.2x\n" , |
924 | block_id); |
925 | } |
926 | |
927 | block = next_block; |
928 | is_first = false; |
929 | } |
930 | |
931 | return 0; |
932 | } |
933 | |
934 | /** |
935 | * ccs_data_parse - Parse a CCS static data file into a usable in-memory |
936 | * data structure |
937 | * @ccsdata: CCS static data in-memory data structure |
938 | * @data: CCS static data binary |
939 | * @len: Length of @data |
940 | * @dev: Device the data is related to (used for printing debug messages) |
941 | * @verbose: Whether to be verbose or not |
942 | */ |
943 | int ccs_data_parse(struct ccs_data_container *ccsdata, const void *data, |
944 | size_t len, struct device *dev, bool verbose) |
945 | { |
946 | struct bin_container bin = { 0 }; |
947 | int rval; |
948 | |
949 | rval = __ccs_data_parse(bin: &bin, ccsdata, data, len, dev, verbose); |
950 | if (rval) |
951 | return rval; |
952 | |
953 | rval = bin_backing_alloc(bin: &bin); |
954 | if (rval) |
955 | return rval; |
956 | |
957 | rval = __ccs_data_parse(bin: &bin, ccsdata, data, len, dev, verbose: false); |
958 | if (rval) |
959 | goto out_free; |
960 | |
961 | if (verbose && ccsdata->version) |
962 | print_ccs_data_version(dev, v: ccsdata->version); |
963 | |
964 | if (bin.now != bin.end) { |
965 | rval = -EPROTO; |
966 | dev_dbg(dev, "parsing mismatch; base %p; now %p; end %p\n" , |
967 | bin.base, bin.now, bin.end); |
968 | goto out_free; |
969 | } |
970 | |
971 | ccsdata->backing = bin.base; |
972 | |
973 | return 0; |
974 | |
975 | out_free: |
976 | kvfree(addr: bin.base); |
977 | |
978 | return rval; |
979 | } |
980 | |