kernel: mtdsplit: Add support for D-link JBOOT
[openwrt/openwrt.git] / target / linux / generic / files / drivers / mtd / mtdsplit / mtdsplit_jimage.c
1 /*
2 * Copyright (C) 2018 PaweĊ‚ Dembicki <paweldembicki@gmail.com>
3 *
4 * Based on: mtdsplit_uimage.c
5 * Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
6 *
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License version 2 as published
9 * by the Free Software Foundation.
10 *
11 */
12
13 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14
15 #include <linux/module.h>
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/slab.h>
19 #include <linux/vmalloc.h>
20 #include <linux/mtd/mtd.h>
21 #include <linux/mtd/partitions.h>
22 #include <linux/byteorder/generic.h>
23
24 #include "mtdsplit.h"
25
26 #define MAX_HEADER_LEN ( STAG_SIZE + SCH2_SIZE )
27
28 #define STAG_SIZE 16
29 #define STAG_ID 0x04
30 #define STAG_MAGIC 0x2B24
31
32 #define SCH2_SIZE 40
33 #define SCH2_MAGIC 0x2124
34 #define SCH2_VER 0x02
35
36 /*
37 * Jboot image header,
38 * all data in little endian.
39 */
40
41 struct jimage_header //stag + sch2 jboot joined headers
42 {
43 uint8_t stag_cmark; // in factory 0xFF , in sysupgrade must be the same as stag_id
44 uint8_t stag_id; // 0x04
45 uint16_t stag_magic; //magic 0x2B24
46 uint32_t stag_time_stamp; // timestamp calculated in jboot way
47 uint32_t stag_image_length; // lentgh of kernel + sch2 header
48 uint16_t stag_image_checksum; // negated jboot_checksum of sch2 + kernel
49 uint16_t stag_tag_checksum; // negated jboot_checksum of stag header data
50 uint16_t sch2_magic; // magic 0x2124
51 uint8_t sch2_cp_type; // 0x00 for flat, 0x01 for jz, 0x02 for gzip, 0x03 for lzma
52 uint8_t sch2_version; // 0x02 for sch2
53 uint32_t sch2_ram_addr; // ram entry address
54 uint32_t sch2_image_len; // kernel image length
55 uint32_t sch2_image_crc32; // kernel image crc
56 uint32_t sch2_start_addr; // ram start address
57 uint32_t sch2_rootfs_addr; // rootfs flash address
58 uint32_t sch2_rootfs_len; // rootfls length
59 uint32_t sch2_rootfs_crc32; // rootfs crc32
60 uint32_t sch2_header_crc32; // sch2 header crc32, durring calculation this area is replaced by zero
61 uint16_t sch2_header_length; // sch2 header length: 0x28
62 uint16_t sch2_cmd_line_length; // cmd line length, known zeros
63 };
64
65 static int
66 read_jimage_header(struct mtd_info *mtd, size_t offset, u_char *buf,
67 size_t header_len)
68 {
69 size_t retlen;
70 int ret;
71
72 ret = mtd_read(mtd, offset, header_len, &retlen, buf);
73 if (ret) {
74 pr_debug("read error in \"%s\"\n", mtd->name);
75 return ret;
76 }
77
78 if (retlen != header_len) {
79 pr_debug("short read in \"%s\"\n", mtd->name);
80 return -EIO;
81 }
82
83 return 0;
84 }
85
86 /**
87 * __mtdsplit_parse_jimage - scan partition and create kernel + rootfs parts
88 *
89 * @find_header: function to call for a block of data that will return offset
90 * of a valid jImage header if found
91 */
92 static int __mtdsplit_parse_jimage(struct mtd_info *master,
93 const struct mtd_partition **pparts,
94 struct mtd_part_parser_data *data,
95 ssize_t (*find_header)(u_char *buf, size_t len))
96 {
97 struct mtd_partition *parts;
98 u_char *buf;
99 int nr_parts;
100 size_t offset;
101 size_t jimage_offset;
102 size_t jimage_size = 0;
103 size_t rootfs_offset;
104 size_t rootfs_size = 0;
105 int jimage_part, rf_part;
106 int ret;
107 enum mtdsplit_part_type type;
108
109 nr_parts = 2;
110 parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
111 if (!parts)
112 return -ENOMEM;
113
114 buf = vmalloc(MAX_HEADER_LEN);
115 if (!buf) {
116 ret = -ENOMEM;
117 goto err_free_parts;
118 }
119
120 /* find jImage on erase block boundaries */
121 for (offset = 0; offset < master->size; offset += master->erasesize) {
122 struct jimage_header *header;
123
124 jimage_size = 0;
125
126 ret = read_jimage_header(master, offset, buf, MAX_HEADER_LEN);
127 if (ret)
128 continue;
129
130 ret = find_header(buf, MAX_HEADER_LEN);
131 if (ret < 0) {
132 pr_debug("no valid jImage found in \"%s\" at offset %llx\n",
133 master->name, (unsigned long long) offset);
134 continue;
135 }
136 header = (struct jimage_header *)(buf + ret);
137
138 jimage_size = sizeof(*header) + header->sch2_image_len + ret;
139 if ((offset + jimage_size) > master->size) {
140 pr_debug("jImage exceeds MTD device \"%s\"\n",
141 master->name);
142 continue;
143 }
144 break;
145 }
146
147 if (jimage_size == 0) {
148 pr_debug("no jImage found in \"%s\"\n", master->name);
149 ret = -ENODEV;
150 goto err_free_buf;
151 }
152
153 jimage_offset = offset;
154
155 if (jimage_offset == 0) {
156 jimage_part = 0;
157 rf_part = 1;
158
159 /* find the roots after the jImage */
160 ret = mtd_find_rootfs_from(master, jimage_offset + jimage_size,
161 master->size, &rootfs_offset, &type);
162 if (ret) {
163 pr_debug("no rootfs after jImage in \"%s\"\n",
164 master->name);
165 goto err_free_buf;
166 }
167
168 rootfs_size = master->size - rootfs_offset;
169 jimage_size = rootfs_offset - jimage_offset;
170 } else {
171 rf_part = 0;
172 jimage_part = 1;
173
174 /* check rootfs presence at offset 0 */
175 ret = mtd_check_rootfs_magic(master, 0, &type);
176 if (ret) {
177 pr_debug("no rootfs before jImage in \"%s\"\n",
178 master->name);
179 goto err_free_buf;
180 }
181
182 rootfs_offset = 0;
183 rootfs_size = jimage_offset;
184 }
185
186 if (rootfs_size == 0) {
187 pr_debug("no rootfs found in \"%s\"\n", master->name);
188 ret = -ENODEV;
189 goto err_free_buf;
190 }
191
192 parts[jimage_part].name = KERNEL_PART_NAME;
193 parts[jimage_part].offset = jimage_offset;
194 parts[jimage_part].size = jimage_size;
195
196 if (type == MTDSPLIT_PART_TYPE_UBI)
197 parts[rf_part].name = UBI_PART_NAME;
198 else
199 parts[rf_part].name = ROOTFS_PART_NAME;
200 parts[rf_part].offset = rootfs_offset;
201 parts[rf_part].size = rootfs_size;
202
203 vfree(buf);
204
205 *pparts = parts;
206 return nr_parts;
207
208 err_free_buf:
209 vfree(buf);
210
211 err_free_parts:
212 kfree(parts);
213 return ret;
214 }
215
216 static ssize_t jimage_verify_default(u_char *buf, size_t len)
217 {
218 struct jimage_header *header = (struct jimage_header *)buf;
219
220 /* default sanity checks */
221 if (header->stag_magic != STAG_MAGIC) {
222 pr_debug("invalid jImage stag header magic: %04x\n",
223 header->stag_magic);
224 return -EINVAL;
225 }
226 if (header->sch2_magic != SCH2_MAGIC) {
227 pr_debug("invalid jImage sch2 header magic: %04x\n",
228 header->stag_magic);
229 return -EINVAL;
230 }
231 if (header->stag_cmark != header->stag_id) {
232 pr_debug("invalid jImage stag header cmark: %02x\n",
233 header->stag_magic);
234 return -EINVAL;
235 }
236 if (header->stag_id != STAG_ID) {
237 pr_debug("invalid jImage stag header id: %02x\n",
238 header->stag_magic);
239 return -EINVAL;
240 }
241 if (header->sch2_version != SCH2_VER) {
242 pr_debug("invalid jImage sch2 header version: %02x\n",
243 header->stag_magic);
244 return -EINVAL;
245 }
246
247 return 0;
248 }
249
250 static int
251 mtdsplit_jimage_parse_generic(struct mtd_info *master,
252 const struct mtd_partition **pparts,
253 struct mtd_part_parser_data *data)
254 {
255 return __mtdsplit_parse_jimage(master, pparts, data,
256 jimage_verify_default);
257 }
258
259 static struct mtd_part_parser jimage_generic_parser = {
260 .owner = THIS_MODULE,
261 .name = "jimage-fw",
262 .parse_fn = mtdsplit_jimage_parse_generic,
263 .type = MTD_PARSER_TYPE_FIRMWARE,
264 };
265
266 /**************************************************
267 * Init
268 **************************************************/
269
270 static int __init mtdsplit_jimage_init(void)
271 {
272 register_mtd_parser(&jimage_generic_parser);
273
274 return 0;
275 }
276
277 module_init(mtdsplit_jimage_init);