ar71xx: mikrotik: bypass id check in __rb_get_wlan_data()
[oweals/openwrt.git] / target / linux / ar71xx / files / arch / mips / ath79 / routerboot.c
1 /*
2  *  RouterBoot helper routines
3  *
4  *  Copyright (C) 2012 Gabor Juhos <juhosg@openwrt.org>
5  *
6  *  This program is free software; you can redistribute it and/or modify it
7  *  under the terms of the GNU General Public License version 2 as published
8  *  by the Free Software Foundation.
9  */
10
11 #define pr_fmt(fmt) "rb: " fmt
12
13 #include <linux/kernel.h>
14 #include <linux/kobject.h>
15 #include <linux/slab.h>
16 #include <linux/errno.h>
17 #include <linux/routerboot.h>
18 #include <linux/rle.h>
19 #include <linux/lzo.h>
20
21 #include "routerboot.h"
22
23 #define RB_BLOCK_SIZE           0x1000
24 #define RB_ART_SIZE             0x10000
25 #define RB_MAGIC_ERD            0x00455244      /* extended radio data */
26
27 static struct rb_info rb_info;
28
29 static u32 get_u32(void *buf)
30 {
31         u8 *p = buf;
32
33         return ((u32) p[3] + ((u32) p[2] << 8) + ((u32) p[1] << 16) +
34                ((u32) p[0] << 24));
35 }
36
37 static u16 get_u16(void *buf)
38 {
39         u8 *p = buf;
40
41         return (u16) p[1] + ((u16) p[0] << 8);
42 }
43
44 __init int
45 routerboot_find_magic(u8 *buf, unsigned int buflen, u32 *offset, bool hard)
46 {
47         u32 magic_ref = hard ? RB_MAGIC_HARD : RB_MAGIC_SOFT;
48         u32 magic;
49         u32 cur = *offset;
50
51         while (cur < buflen) {
52                 magic = get_u32(buf + cur);
53                 if (magic == magic_ref) {
54                         *offset = cur;
55                         return 0;
56                 }
57
58                 cur += 0x1000;
59         }
60
61         return -ENOENT;
62 }
63
64 __init int
65 routerboot_find_tag(u8 *buf, unsigned int buflen, u16 tag_id,
66                     u8 **tag_data, u16 *tag_len)
67 {
68         uint32_t magic;
69         bool align = false;
70         int ret;
71
72         if (buflen < 4)
73                 return -EINVAL;
74
75         magic = get_u32(buf);
76         switch (magic) {
77         case RB_MAGIC_ERD:
78                 align = true;
79                 /* fall trough */
80         case RB_MAGIC_HARD:
81                 /* skip magic value */
82                 buf += 4;
83                 buflen -= 4;
84                 break;
85
86         case RB_MAGIC_SOFT:
87                 if (buflen < 8)
88                         return -EINVAL;
89
90                 /* skip magic and CRC value */
91                 buf += 8;
92                 buflen -= 8;
93
94                 break;
95
96         default:
97                 return -EINVAL;
98         }
99
100         ret = -ENOENT;
101         while (buflen > 2) {
102                 u16 id;
103                 u16 len;
104
105                 len = get_u16(buf);
106                 buf += 2;
107                 buflen -= 2;
108
109                 if (buflen < 2)
110                         break;
111
112                 id = get_u16(buf);
113                 buf += 2;
114                 buflen -= 2;
115
116                 if (id == RB_ID_TERMINATOR)
117                         break;
118
119                 if (buflen < len)
120                         break;
121
122                 if (id == tag_id) {
123                         if (tag_len)
124                                 *tag_len = len;
125                         if (tag_data)
126                                 *tag_data = buf;
127                         ret = 0;
128                         break;
129                 }
130
131                 if (align)
132                         len = (len + 3) / 4;
133
134                 buf += len;
135                 buflen -= len;
136         }
137
138         return ret;
139 }
140
141 static inline int
142 rb_find_hard_cfg_tag(u16 tag_id, u8 **tag_data, u16 *tag_len)
143 {
144         if (!rb_info.hard_cfg_data ||
145             !rb_info.hard_cfg_size)
146                 return -ENOENT;
147
148         return routerboot_find_tag(rb_info.hard_cfg_data,
149                                    rb_info.hard_cfg_size,
150                                    tag_id, tag_data, tag_len);
151 }
152
153 __init const char *
154 rb_get_board_name(void)
155 {
156         u16 tag_len;
157         u8 *tag;
158         int err;
159
160         err = rb_find_hard_cfg_tag(RB_ID_BOARD_NAME, &tag, &tag_len);
161         if (err)
162                 return NULL;
163
164         return tag;
165 }
166
167 __init u32
168 rb_get_hw_options(void)
169 {
170         u16 tag_len;
171         u8 *tag;
172         int err;
173
174         err = rb_find_hard_cfg_tag(RB_ID_HW_OPTIONS, &tag, &tag_len);
175         if (err)
176                 return 0;
177
178         return get_u32(tag);
179 }
180
181 static void * __init
182 __rb_get_wlan_data(u16 id)
183 {
184         u16 tag_len;
185         u8 *tag;
186         void *buf;
187         int err;
188         u32 magic;
189         size_t src_done;
190         size_t dst_done;
191
192         err = rb_find_hard_cfg_tag(RB_ID_WLAN_DATA, &tag, &tag_len);
193         if (err) {
194                 pr_err("no calibration data found\n");
195                 goto err;
196         }
197
198         buf = kmalloc(RB_ART_SIZE, GFP_KERNEL);
199         if (buf == NULL) {
200                 pr_err("no memory for calibration data\n");
201                 goto err;
202         }
203
204         magic = get_u32(tag);
205         if (magic == RB_MAGIC_ERD) {
206                 u8 *erd_data;
207                 u16 erd_len;
208
209                 err = routerboot_find_tag(tag, tag_len, 0x1,
210                                           &erd_data, &erd_len);
211                 if (err) {
212                         pr_err("no ERD data found for id %u\n", id);
213                         goto err_free;
214                 }
215
216                 dst_done = RB_ART_SIZE;
217                 err = lzo1x_decompress_safe(erd_data, erd_len, buf, &dst_done);
218                 if (err) {
219                         pr_err("unable to decompress calibration data %d\n",
220                                err);
221                         goto err_free;
222                 }
223         } else {
224                 err = rle_decode((char *) tag, tag_len, buf, RB_ART_SIZE,
225                                  &src_done, &dst_done);
226                 if (err) {
227                         pr_err("unable to decode calibration data\n");
228                         goto err_free;
229                 }
230         }
231
232         return buf;
233
234 err_free:
235         kfree(buf);
236 err:
237         return NULL;
238 }
239
240 __init void *
241 rb_get_wlan_data(void)
242 {
243         return __rb_get_wlan_data(0);
244 }
245
246 __init void *
247 rb_get_ext_wlan_data(u16 id)
248 {
249         return __rb_get_wlan_data(id);
250 }
251
252 __init const struct rb_info *
253 rb_init_info(void *data, unsigned int size)
254 {
255         unsigned int offset;
256
257         if (size == 0 || (size % RB_BLOCK_SIZE) != 0)
258                 return NULL;
259
260         for (offset = 0; offset < size; offset += RB_BLOCK_SIZE) {
261                 u32 magic;
262
263                 magic = get_u32(data + offset);
264                 switch (magic) {
265                 case RB_MAGIC_HARD:
266                         rb_info.hard_cfg_offs = offset;
267                         break;
268
269                 case RB_MAGIC_SOFT:
270                         rb_info.soft_cfg_offs = offset;
271                         break;
272                 }
273         }
274
275         if (!rb_info.hard_cfg_offs) {
276                 pr_err("could not find a valid RouterBOOT hard config\n");
277                 return NULL;
278         }
279
280         if (!rb_info.soft_cfg_offs) {
281                 pr_err("could not find a valid RouterBOOT soft config\n");
282                 return NULL;
283         }
284
285         rb_info.hard_cfg_size = RB_BLOCK_SIZE;
286         rb_info.hard_cfg_data = kmemdup(data + rb_info.hard_cfg_offs,
287                                         RB_BLOCK_SIZE, GFP_KERNEL);
288         if (!rb_info.hard_cfg_data)
289                 return NULL;
290
291         rb_info.board_name = rb_get_board_name();
292         rb_info.hw_options = rb_get_hw_options();
293
294         return &rb_info;
295 }
296
297 #if 0
298 static char *rb_ext_wlan_data;
299
300 static ssize_t
301 rb_ext_wlan_data_read(struct file *filp, struct kobject *kobj,
302                       struct bin_attribute *attr, char *buf,
303                       loff_t off, size_t count)
304 {
305          if (off + count > attr->size)
306                  return -EFBIG;
307
308          memcpy(buf, &rb_ext_wlan_data[off], count);
309
310          return count;
311 }
312
313 static const struct bin_attribute rb_ext_wlan_data_attr = {
314         .attr = {
315                 .name = "ext_wlan_data",
316                 .mode = S_IRUSR | S_IWUSR,
317         },
318         .read = rb_ext_wlan_data_read,
319         .size = RB_ART_SIZE,
320 };
321
322 static int __init rb_sysfs_init(void)
323 {
324         struct kobject *rb_kobj;
325         int ret;
326
327         rb_ext_wlan_data = rb_get_ext_wlan_data(1);
328         if (rb_ext_wlan_data == NULL)
329                 return -ENOENT;
330
331         rb_kobj = kobject_create_and_add("routerboot", firmware_kobj);
332         if (rb_kobj == NULL) {
333                 ret = -ENOMEM;
334                 pr_err("unable to create sysfs entry\n");
335                 goto err_free_wlan_data;
336         }
337
338         ret = sysfs_create_bin_file(rb_kobj, &rb_ext_wlan_data_attr);
339         if (ret) {
340                 pr_err("unable to create sysfs file, %d\n", ret);
341                 goto err_put_kobj;
342         }
343
344         return 0;
345
346 err_put_kobj:
347         kobject_put(rb_kobj);
348 err_free_wlan_data:
349         kfree(rb_ext_wlan_data);
350         return ret;
351 }
352
353 late_initcall(rb_sysfs_init);
354 #endif