1*4882a593Smuzhiyun /* SPDX-License-Identifier: GPL-2.0 */
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Compact binary representation of ihex records. Some devices need their
4*4882a593Smuzhiyun * firmware loaded in strange orders rather than a single big blob, but
5*4882a593Smuzhiyun * actually parsing ihex-as-text within the kernel seems silly. Thus,...
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun #ifndef __LINUX_IHEX_H__
9*4882a593Smuzhiyun #define __LINUX_IHEX_H__
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/types.h>
12*4882a593Smuzhiyun #include <linux/firmware.h>
13*4882a593Smuzhiyun #include <linux/device.h>
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun /* Intel HEX files actually limit the length to 256 bytes, but we have
16*4882a593Smuzhiyun drivers which would benefit from using separate records which are
17*4882a593Smuzhiyun longer than that, so we extend to 16 bits of length */
18*4882a593Smuzhiyun struct ihex_binrec {
19*4882a593Smuzhiyun __be32 addr;
20*4882a593Smuzhiyun __be16 len;
21*4882a593Smuzhiyun uint8_t data[];
22*4882a593Smuzhiyun } __attribute__((packed));
23*4882a593Smuzhiyun
ihex_binrec_size(const struct ihex_binrec * p)24*4882a593Smuzhiyun static inline uint16_t ihex_binrec_size(const struct ihex_binrec *p)
25*4882a593Smuzhiyun {
26*4882a593Smuzhiyun return be16_to_cpu(p->len) + sizeof(*p);
27*4882a593Smuzhiyun }
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /* Find the next record, taking into account the 4-byte alignment */
30*4882a593Smuzhiyun static inline const struct ihex_binrec *
__ihex_next_binrec(const struct ihex_binrec * rec)31*4882a593Smuzhiyun __ihex_next_binrec(const struct ihex_binrec *rec)
32*4882a593Smuzhiyun {
33*4882a593Smuzhiyun const void *p = rec;
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun return p + ALIGN(ihex_binrec_size(rec), 4);
36*4882a593Smuzhiyun }
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun static inline const struct ihex_binrec *
ihex_next_binrec(const struct ihex_binrec * rec)39*4882a593Smuzhiyun ihex_next_binrec(const struct ihex_binrec *rec)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun rec = __ihex_next_binrec(rec);
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun return be16_to_cpu(rec->len) ? rec : NULL;
44*4882a593Smuzhiyun }
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun /* Check that ihex_next_binrec() won't take us off the end of the image... */
ihex_validate_fw(const struct firmware * fw)47*4882a593Smuzhiyun static inline int ihex_validate_fw(const struct firmware *fw)
48*4882a593Smuzhiyun {
49*4882a593Smuzhiyun const struct ihex_binrec *end, *rec;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun rec = (const void *)fw->data;
52*4882a593Smuzhiyun end = (const void *)&fw->data[fw->size - sizeof(*end)];
53*4882a593Smuzhiyun
54*4882a593Smuzhiyun for (; rec <= end; rec = __ihex_next_binrec(rec)) {
55*4882a593Smuzhiyun /* Zero length marks end of records */
56*4882a593Smuzhiyun if (rec == end && !be16_to_cpu(rec->len))
57*4882a593Smuzhiyun return 0;
58*4882a593Smuzhiyun }
59*4882a593Smuzhiyun return -EINVAL;
60*4882a593Smuzhiyun }
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun /* Request firmware and validate it so that we can trust we won't
63*4882a593Smuzhiyun * run off the end while reading records... */
request_ihex_firmware(const struct firmware ** fw,const char * fw_name,struct device * dev)64*4882a593Smuzhiyun static inline int request_ihex_firmware(const struct firmware **fw,
65*4882a593Smuzhiyun const char *fw_name,
66*4882a593Smuzhiyun struct device *dev)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun const struct firmware *lfw;
69*4882a593Smuzhiyun int ret;
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun ret = request_firmware(&lfw, fw_name, dev);
72*4882a593Smuzhiyun if (ret)
73*4882a593Smuzhiyun return ret;
74*4882a593Smuzhiyun ret = ihex_validate_fw(lfw);
75*4882a593Smuzhiyun if (ret) {
76*4882a593Smuzhiyun dev_err(dev, "Firmware \"%s\" not valid IHEX records\n",
77*4882a593Smuzhiyun fw_name);
78*4882a593Smuzhiyun release_firmware(lfw);
79*4882a593Smuzhiyun return ret;
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun *fw = lfw;
82*4882a593Smuzhiyun return 0;
83*4882a593Smuzhiyun }
84*4882a593Smuzhiyun #endif /* __LINUX_IHEX_H__ */
85