xref: /OK3568_Linux_fs/kernel/include/linux/ihex.h (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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