tools/elfutils: refresh portability patch for macOS
[openwrt/openwrt.git] / target / linux / generic / files / drivers / mtd / mtdsplit / mtdsplit_trx.c
1 /*
2 * Copyright (C) 2013 Gabor Juhos <juhosg@openwrt.org>
3 * Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 as published
7 * by the Free Software Foundation.
8 *
9 */
10
11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
12
13 #include <linux/module.h>
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/slab.h>
17 #include <linux/mtd/mtd.h>
18 #include <linux/mtd/partitions.h>
19 #include <linux/byteorder/generic.h>
20 #include <linux/of.h>
21
22 #include "mtdsplit.h"
23
24 #define TRX_MAGIC 0x30524448 /* "HDR0" */
25
26 struct trx_header {
27 __le32 magic;
28 __le32 len;
29 __le32 crc32;
30 __le32 flag_version;
31 __le32 offset[4];
32 };
33
34 static int
35 read_trx_header(struct mtd_info *mtd, size_t offset,
36 struct trx_header *header)
37 {
38 size_t header_len;
39 size_t retlen;
40 int ret;
41
42 header_len = sizeof(*header);
43 ret = mtd_read(mtd, offset, header_len, &retlen,
44 (unsigned char *) header);
45 if (ret) {
46 pr_debug("read error in \"%s\"\n", mtd->name);
47 return ret;
48 }
49
50 if (retlen != header_len) {
51 pr_debug("short read in \"%s\"\n", mtd->name);
52 return -EIO;
53 }
54
55 return 0;
56 }
57
58 static int
59 mtdsplit_parse_trx(struct mtd_info *master,
60 const struct mtd_partition **pparts,
61 struct mtd_part_parser_data *data)
62 {
63 struct mtd_partition *parts;
64 struct trx_header hdr;
65 int nr_parts;
66 size_t offset;
67 size_t trx_offset;
68 size_t trx_size = 0;
69 size_t rootfs_offset;
70 size_t rootfs_size = 0;
71 int ret;
72
73 nr_parts = 2;
74 parts = kzalloc(nr_parts * sizeof(*parts), GFP_KERNEL);
75 if (!parts)
76 return -ENOMEM;
77
78 /* find trx image on erase block boundaries */
79 for (offset = 0; offset < master->size; offset += master->erasesize) {
80 trx_size = 0;
81
82 ret = read_trx_header(master, offset, &hdr);
83 if (ret)
84 continue;
85
86 if (hdr.magic != cpu_to_le32(TRX_MAGIC)) {
87 pr_debug("no valid trx header found in \"%s\" at offset %llx\n",
88 master->name, (unsigned long long) offset);
89 continue;
90 }
91
92 trx_size = le32_to_cpu(hdr.len);
93 if ((offset + trx_size) > master->size) {
94 pr_debug("trx image exceeds MTD device \"%s\"\n",
95 master->name);
96 continue;
97 }
98 break;
99 }
100
101 if (trx_size == 0) {
102 pr_debug("no trx header found in \"%s\"\n", master->name);
103 ret = -ENODEV;
104 goto err;
105 }
106
107 trx_offset = offset + hdr.offset[0];
108 rootfs_offset = offset + hdr.offset[1];
109 rootfs_size = master->size - rootfs_offset;
110 trx_size = rootfs_offset - trx_offset;
111
112 if (rootfs_size == 0) {
113 pr_debug("no rootfs found in \"%s\"\n", master->name);
114 ret = -ENODEV;
115 goto err;
116 }
117
118 parts[0].name = KERNEL_PART_NAME;
119 parts[0].offset = trx_offset;
120 parts[0].size = trx_size;
121
122 parts[1].name = ROOTFS_PART_NAME;
123 parts[1].offset = rootfs_offset;
124 parts[1].size = rootfs_size;
125
126 *pparts = parts;
127 return nr_parts;
128
129 err:
130 kfree(parts);
131 return ret;
132 }
133
134 static const struct of_device_id trx_parser_of_match_table[] = {
135 { .compatible = "openwrt,trx" },
136 {},
137 };
138 MODULE_DEVICE_TABLE(of, trx_parser_of_match_table);
139
140 static struct mtd_part_parser trx_parser = {
141 .owner = THIS_MODULE,
142 .name = "trx-fw",
143 .of_match_table = trx_parser_of_match_table,
144 .parse_fn = mtdsplit_parse_trx,
145 .type = MTD_PARSER_TYPE_FIRMWARE,
146 };
147
148 static int __init mtdsplit_trx_init(void)
149 {
150 register_mtd_parser(&trx_parser);
151
152 return 0;
153 }
154
155 module_init(mtdsplit_trx_init);