xref: /openbmc/linux/include/linux/ihex.h (revision 1d9e13e8)
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