1b2441318SGreg Kroah-Hartman /* SPDX-License-Identifier: GPL-2.0 */
2bacfe09dSDavid Woodhouse /*
3bacfe09dSDavid Woodhouse * Compact binary representation of ihex records. Some devices need their
4bacfe09dSDavid Woodhouse * firmware loaded in strange orders rather than a single big blob, but
5bacfe09dSDavid Woodhouse * actually parsing ihex-as-text within the kernel seems silly. Thus,...
6bacfe09dSDavid Woodhouse */
7bacfe09dSDavid Woodhouse
8bacfe09dSDavid Woodhouse #ifndef __LINUX_IHEX_H__
9bacfe09dSDavid Woodhouse #define __LINUX_IHEX_H__
10bacfe09dSDavid Woodhouse
11bacfe09dSDavid Woodhouse #include <linux/types.h>
12bacfe09dSDavid Woodhouse #include <linux/firmware.h>
13f1485f3dSDavid Woodhouse #include <linux/device.h>
14bacfe09dSDavid Woodhouse
15bacfe09dSDavid Woodhouse /* Intel HEX files actually limit the length to 256 bytes, but we have
16bacfe09dSDavid Woodhouse drivers which would benefit from using separate records which are
17bacfe09dSDavid Woodhouse longer than that, so we extend to 16 bits of length */
18bacfe09dSDavid Woodhouse struct ihex_binrec {
19bacfe09dSDavid Woodhouse __be32 addr;
20bacfe09dSDavid Woodhouse __be16 len;
211d9e13e8SGustavo A. R. Silva uint8_t data[];
2285ebd003SMarc Zyngier } __attribute__((packed));
23bacfe09dSDavid Woodhouse
ihex_binrec_size(const struct ihex_binrec * p)249fb4ab4dSAndrey Smirnov static inline uint16_t ihex_binrec_size(const struct ihex_binrec *p)
259fb4ab4dSAndrey Smirnov {
269fb4ab4dSAndrey Smirnov return be16_to_cpu(p->len) + sizeof(*p);
279fb4ab4dSAndrey Smirnov }
289fb4ab4dSAndrey Smirnov
29bacfe09dSDavid Woodhouse /* Find the next record, taking into account the 4-byte alignment */
30bacfe09dSDavid Woodhouse static inline const struct ihex_binrec *
__ihex_next_binrec(const struct ihex_binrec * rec)318092e792SAndrey Smirnov __ihex_next_binrec(const struct ihex_binrec *rec)
32bacfe09dSDavid Woodhouse {
339fb4ab4dSAndrey Smirnov const void *p = rec;
34bacfe09dSDavid Woodhouse
359fb4ab4dSAndrey Smirnov return p + ALIGN(ihex_binrec_size(rec), 4);
368092e792SAndrey Smirnov }
378092e792SAndrey Smirnov
388092e792SAndrey Smirnov static inline const struct ihex_binrec *
ihex_next_binrec(const struct ihex_binrec * rec)398092e792SAndrey Smirnov ihex_next_binrec(const struct ihex_binrec *rec)
408092e792SAndrey Smirnov {
418092e792SAndrey Smirnov rec = __ihex_next_binrec(rec);
428092e792SAndrey Smirnov
43bacfe09dSDavid Woodhouse return be16_to_cpu(rec->len) ? rec : NULL;
44bacfe09dSDavid Woodhouse }
45bacfe09dSDavid Woodhouse
46bacfe09dSDavid Woodhouse /* Check that ihex_next_binrec() won't take us off the end of the image... */
ihex_validate_fw(const struct firmware * fw)47bacfe09dSDavid Woodhouse static inline int ihex_validate_fw(const struct firmware *fw)
48bacfe09dSDavid Woodhouse {
498092e792SAndrey Smirnov const struct ihex_binrec *end, *rec;
50bacfe09dSDavid Woodhouse
518092e792SAndrey Smirnov rec = (const void *)fw->data;
528092e792SAndrey Smirnov end = (const void *)&fw->data[fw->size - sizeof(*end)];
53bacfe09dSDavid Woodhouse
548092e792SAndrey Smirnov for (; rec <= end; rec = __ihex_next_binrec(rec)) {
55bacfe09dSDavid Woodhouse /* Zero length marks end of records */
565158c36eSAndrey Smirnov if (rec == end && !be16_to_cpu(rec->len))
57bacfe09dSDavid Woodhouse return 0;
58bacfe09dSDavid Woodhouse }
59bacfe09dSDavid Woodhouse return -EINVAL;
60bacfe09dSDavid Woodhouse }
61f1485f3dSDavid Woodhouse
62f1485f3dSDavid Woodhouse /* Request firmware and validate it so that we can trust we won't
63f1485f3dSDavid Woodhouse * run off the end while reading records... */
request_ihex_firmware(const struct firmware ** fw,const char * fw_name,struct device * dev)64f1485f3dSDavid Woodhouse static inline int request_ihex_firmware(const struct firmware **fw,
65f1485f3dSDavid Woodhouse const char *fw_name,
66f1485f3dSDavid Woodhouse struct device *dev)
67f1485f3dSDavid Woodhouse {
68f1485f3dSDavid Woodhouse const struct firmware *lfw;
69f1485f3dSDavid Woodhouse int ret;
70f1485f3dSDavid Woodhouse
71f1485f3dSDavid Woodhouse ret = request_firmware(&lfw, fw_name, dev);
72f1485f3dSDavid Woodhouse if (ret)
73f1485f3dSDavid Woodhouse return ret;
74f1485f3dSDavid Woodhouse ret = ihex_validate_fw(lfw);
75f1485f3dSDavid Woodhouse if (ret) {
76f1485f3dSDavid Woodhouse dev_err(dev, "Firmware \"%s\" not valid IHEX records\n",
77f1485f3dSDavid Woodhouse fw_name);
78f1485f3dSDavid Woodhouse release_firmware(lfw);
79f1485f3dSDavid Woodhouse return ret;
80f1485f3dSDavid Woodhouse }
81f1485f3dSDavid Woodhouse *fw = lfw;
82f1485f3dSDavid Woodhouse return 0;
83f1485f3dSDavid Woodhouse }
84bacfe09dSDavid Woodhouse #endif /* __LINUX_IHEX_H__ */
85