1 // SPDX-License-Identifier: GPL-2.0
3 * Driver for MikroTik RouterBoot hard config.
5 * Copyright (C) 2020 Thibaut VARĂˆNE <hacks+kernel@slashdirt.org>
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.
11 * This driver exposes the data encoded in the "hard_config" flash segment of
12 * MikroTik RouterBOARDs devices. It presents the data in a sysfs folder
13 * named "hard_config". The WLAN calibration data is available on demand via
14 * the 'wlan_data' sysfs file in that folder.
16 * This driver permanently allocates a chunk of RAM as large as the hard_config
17 * MTD partition, although it is technically possible to operate entirely from
18 * the MTD device without using a local buffer (except when requesting WLAN
19 * calibration data), at the cost of a performance penalty.
21 * Some constant defines extracted from routerboot.{c,h} by Gabor Juhos
22 * <juhosg@openwrt.org>
25 #include <linux/types.h>
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/slab.h>
29 #include <linux/errno.h>
30 #include <linux/kobject.h>
31 #include <linux/bitops.h>
32 #include <linux/string.h>
33 #include <linux/mtd/mtd.h>
34 #include <linux/sysfs.h>
35 #include <linux/lzo.h>
37 #include "routerboot.h"
39 #define RB_HARDCONFIG_VER "0.01"
40 #define RB_HC_PR_PFX "[rb_hardconfig] "
42 /* ID values for hardware settings */
43 #define RB_ID_FLASH_INFO 0x03
44 #define RB_ID_MAC_ADDRESS_PACK 0x04
45 #define RB_ID_BOARD_PRODUCT_CODE 0x05
46 #define RB_ID_BIOS_VERSION 0x06
47 #define RB_ID_SDRAM_TIMINGS 0x08
48 #define RB_ID_DEVICE_TIMINGS 0x09
49 #define RB_ID_SOFTWARE_ID 0x0A
50 #define RB_ID_SERIAL_NUMBER 0x0B
51 #define RB_ID_MEMORY_SIZE 0x0D
52 #define RB_ID_MAC_ADDRESS_COUNT 0x0E
53 #define RB_ID_HW_OPTIONS 0x15
54 #define RB_ID_WLAN_DATA 0x16
55 #define RB_ID_BOARD_IDENTIFIER 0x17
56 #define RB_ID_PRODUCT_NAME 0x21
57 #define RB_ID_DEFCONF 0x26
59 /* Bit definitions for hardware options */
60 #define RB_HW_OPT_NO_UART BIT(0)
61 #define RB_HW_OPT_HAS_VOLTAGE BIT(1)
62 #define RB_HW_OPT_HAS_USB BIT(2)
63 #define RB_HW_OPT_HAS_ATTINY BIT(3)
64 #define RB_HW_OPT_NO_NAND BIT(14)
65 #define RB_HW_OPT_HAS_LCD BIT(15)
66 #define RB_HW_OPT_HAS_POE_OUT BIT(16)
67 #define RB_HW_OPT_HAS_uSD BIT(17)
68 #define RB_HW_OPT_HAS_SIM BIT(18)
69 #define RB_HW_OPT_HAS_SFP BIT(20)
70 #define RB_HW_OPT_HAS_WIFI BIT(21)
71 #define RB_HW_OPT_HAS_TS_FOR_ADC BIT(22)
72 #define RB_HW_OPT_HAS_PLC BIT(29)
74 static struct kobject
*hc_kobj
;
75 static u8
*hc_buf
; // ro buffer after init(): no locking required
76 static size_t hc_buflen
;
78 /* Array of known hw_options bits with human-friendly parsing */
79 static struct hc_hwopt
{
82 } const hc_hwopts
[] = {
84 .bit
= RB_HW_OPT_NO_UART
,
87 .bit
= RB_HW_OPT_HAS_VOLTAGE
,
90 .bit
= RB_HW_OPT_HAS_USB
,
93 .bit
= RB_HW_OPT_HAS_ATTINY
,
94 .str
= "has ATtiny\t",
96 .bit
= RB_HW_OPT_NO_NAND
,
99 .bit
= RB_HW_OPT_HAS_LCD
,
100 .str
= "has LCD\t\t",
102 .bit
= RB_HW_OPT_HAS_POE_OUT
,
103 .str
= "has POE out\t",
105 .bit
= RB_HW_OPT_HAS_uSD
,
106 .str
= "has MicroSD\t",
108 .bit
= RB_HW_OPT_HAS_SIM
,
109 .str
= "has SIM\t\t",
111 .bit
= RB_HW_OPT_HAS_SFP
,
112 .str
= "has SFP\t\t",
114 .bit
= RB_HW_OPT_HAS_WIFI
,
117 .bit
= RB_HW_OPT_HAS_TS_FOR_ADC
,
118 .str
= "has TS ADC\t",
120 .bit
= RB_HW_OPT_HAS_PLC
,
121 .str
= "has PLC\t\t",
125 static ssize_t
hc_tag_show_string(const u8
*pld
, u16 pld_len
, char *buf
)
127 return snprintf(buf
, pld_len
+1, "%s\n", pld
);
130 static ssize_t
hc_tag_show_u32(const u8
*pld
, u16 pld_len
, char *buf
)
133 u32 data
; // cpu-endian
135 /* Caller ensures pld_len > 0 */
136 if (pld_len
% sizeof(data
))
142 out
+= sprintf(out
, "0x%08x\n", data
);
144 } while ((pld_len
-= sizeof(data
)));
150 * The MAC is stored network-endian on all devices, in 2 32-bit segments:
151 * <XX:XX:XX:XX> <XX:XX:00:00>. Kernel print has us covered.
153 static ssize_t
hc_tag_show_mac(const u8
*pld
, u16 pld_len
, char *buf
)
158 return sprintf(buf
, "%pM\n", pld
);
162 * Print HW options in a human readable way:
163 * The raw number and in decoded form
165 static ssize_t
hc_tag_show_hwoptions(const u8
*pld
, u16 pld_len
, char *buf
)
168 u32 data
; // cpu-endian
171 if (sizeof(data
) != pld_len
)
175 out
+= sprintf(out
, "raw\t\t: 0x%08x\n\n", data
);
177 for (i
= 0; i
< ARRAY_SIZE(hc_hwopts
); i
++)
178 out
+= sprintf(out
, "%s: %s\n", hc_hwopts
[i
].str
,
179 (data
& hc_hwopts
[i
].bit
) ? "true" : "false");
184 static ssize_t
hc_wlan_data_bin_read(struct file
*filp
, struct kobject
*kobj
,
185 struct bin_attribute
*attr
, char *buf
,
186 loff_t off
, size_t count
);
188 static struct hc_wlan_attr
{
189 struct bin_attribute battr
;
192 } hc_wlandata_battr
= {
193 .battr
= __BIN_ATTR(wlan_data
, S_IRUSR
, hc_wlan_data_bin_read
, NULL
, 0),
196 static ssize_t
hc_attr_show(struct kobject
*kobj
, struct kobj_attribute
*attr
,
199 /* Array of known tags to publish in sysfs */
200 static struct hc_attr
{
202 ssize_t (* const tshow
)(const u8
*pld
, u16 pld_len
, char *buf
);
203 struct kobj_attribute kattr
;
208 .tag_id
= RB_ID_FLASH_INFO
,
209 .tshow
= hc_tag_show_u32
,
210 .kattr
= __ATTR(flash_info
, S_IRUSR
, hc_attr_show
, NULL
),
212 .tag_id
= RB_ID_MAC_ADDRESS_PACK
,
213 .tshow
= hc_tag_show_mac
,
214 .kattr
= __ATTR(mac_base
, S_IRUSR
, hc_attr_show
, NULL
),
216 .tag_id
= RB_ID_BOARD_PRODUCT_CODE
,
217 .tshow
= hc_tag_show_string
,
218 .kattr
= __ATTR(board_product_code
, S_IRUSR
, hc_attr_show
, NULL
),
220 .tag_id
= RB_ID_BIOS_VERSION
,
221 .tshow
= hc_tag_show_string
,
222 .kattr
= __ATTR(booter_version
, S_IRUSR
, hc_attr_show
, NULL
),
224 .tag_id
= RB_ID_SERIAL_NUMBER
,
225 .tshow
= hc_tag_show_string
,
226 .kattr
= __ATTR(board_serial
, S_IRUSR
, hc_attr_show
, NULL
),
228 .tag_id
= RB_ID_MEMORY_SIZE
,
229 .tshow
= hc_tag_show_u32
,
230 .kattr
= __ATTR(mem_size
, S_IRUSR
, hc_attr_show
, NULL
),
232 .tag_id
= RB_ID_MAC_ADDRESS_COUNT
,
233 .tshow
= hc_tag_show_u32
,
234 .kattr
= __ATTR(mac_count
, S_IRUSR
, hc_attr_show
, NULL
),
236 .tag_id
= RB_ID_HW_OPTIONS
,
237 .tshow
= hc_tag_show_hwoptions
,
238 .kattr
= __ATTR(hw_options
, S_IRUSR
, hc_attr_show
, NULL
),
240 .tag_id
= RB_ID_WLAN_DATA
,
243 .tag_id
= RB_ID_BOARD_IDENTIFIER
,
244 .tshow
= hc_tag_show_string
,
245 .kattr
= __ATTR(board_identifier
, S_IRUSR
, hc_attr_show
, NULL
),
247 .tag_id
= RB_ID_PRODUCT_NAME
,
248 .tshow
= hc_tag_show_string
,
249 .kattr
= __ATTR(product_name
, S_IRUSR
, hc_attr_show
, NULL
),
251 .tag_id
= RB_ID_DEFCONF
,
252 .tshow
= hc_tag_show_string
,
253 .kattr
= __ATTR(defconf
, S_IRUSR
, hc_attr_show
, NULL
),
258 * If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_ERD, then past
259 * that magic number the payload itself contains a routerboot tag node
260 * locating the LZO-compressed calibration data at id 0x1.
262 static int hc_wlan_data_unpack_erd(const u8
*inbuf
, size_t inlen
,
263 void *outbuf
, size_t *outlen
)
265 u16 lzo_ofs
, lzo_len
;
268 /* Find embedded tag */
269 ret
= routerboot_tag_find(inbuf
, inlen
, 0x1, // always id 1
272 pr_debug(RB_HC_PR_PFX
"ERD data not found\n");
276 if (lzo_len
> inlen
) {
277 pr_debug(RB_HC_PR_PFX
"Invalid ERD data length\n");
282 ret
= lzo1x_decompress_safe(inbuf
+lzo_ofs
, lzo_len
, outbuf
, outlen
);
284 pr_debug(RB_HC_PR_PFX
"LZO decompression error (%d)\n", ret
);
290 static int hc_wlan_data_unpack(const size_t tofs
, size_t tlen
,
291 void *outbuf
, size_t *outlen
)
297 /* Caller ensure tlen > 0. tofs is aligned */
298 if ((tofs
+ tlen
) > hc_buflen
)
301 lbuf
= hc_buf
+ tofs
;
302 magic
= *(u32
*)lbuf
;
308 lbuf
+= sizeof(magic
);
309 tlen
-= sizeof(magic
);
310 ret
= hc_wlan_data_unpack_erd(lbuf
, tlen
, outbuf
, outlen
);
314 * If the RB_ID_WLAN_DATA payload doesn't start with a
315 * magic number, the payload itself is the raw RLE-encoded
318 ret
= routerboot_rle_decode(lbuf
, tlen
, outbuf
, outlen
);
320 pr_debug(RB_HC_PR_PFX
"RLE decoding error (%d)\n", ret
);
327 static ssize_t
hc_attr_show(struct kobject
*kobj
, struct kobj_attribute
*attr
,
330 struct hc_attr
*hc_attr
;
334 hc_attr
= container_of(attr
, typeof(*hc_attr
), kattr
);
336 if (!hc_attr
->pld_len
)
339 pld
= hc_buf
+ hc_attr
->pld_ofs
;
340 pld_len
= hc_attr
->pld_len
;
342 return hc_attr
->tshow(pld
, pld_len
, buf
);
346 * This function will allocate and free memory every time it is called. This
347 * is not the fastest way to do this, but since the data is rarely read (mainly
348 * at boot time to load wlan caldata), this makes it possible to save memory for
351 static ssize_t
hc_wlan_data_bin_read(struct file
*filp
, struct kobject
*kobj
,
352 struct bin_attribute
*attr
, char *buf
,
353 loff_t off
, size_t count
)
355 struct hc_wlan_attr
*hc_wattr
;
360 hc_wattr
= container_of(attr
, typeof(*hc_wattr
), battr
);
362 if (!hc_wattr
->pld_len
)
365 outlen
= RB_ART_SIZE
;
367 /* Don't bother unpacking if the source is already too large */
368 if (hc_wattr
->pld_len
> outlen
)
371 outbuf
= kmalloc(outlen
, GFP_KERNEL
);
375 ret
= hc_wlan_data_unpack(hc_wattr
->pld_ofs
, hc_wattr
->pld_len
, outbuf
, &outlen
);
386 if (off
+ count
> outlen
)
387 count
= outlen
- off
;
389 memcpy(buf
, outbuf
+ off
, count
);
395 int __init
rb_hardconfig_init(struct kobject
*rb_kobj
)
397 struct mtd_info
*mtd
;
398 size_t bytes_read
, buflen
;
403 // TODO allow override
404 mtd
= get_mtd_device_nm(RB_MTD_HARD_CONFIG
);
408 hc_buflen
= mtd
->size
;
409 hc_buf
= kmalloc(hc_buflen
, GFP_KERNEL
);
413 ret
= mtd_read(mtd
, 0, hc_buflen
, &bytes_read
, hc_buf
);
415 if (bytes_read
!= hc_buflen
) {
420 /* Check we have what we expect */
421 magic
= *(const u32
*)hc_buf
;
422 if (RB_MAGIC_HARD
!= magic
) {
428 buf
= hc_buf
+ sizeof(magic
);
429 buflen
= hc_buflen
- sizeof(magic
);
433 hc_kobj
= kobject_create_and_add(RB_MTD_HARD_CONFIG
, rb_kobj
);
437 /* Locate and publish all known tags */
438 for (i
= 0; i
< ARRAY_SIZE(hc_attrs
); i
++) {
439 ret
= routerboot_tag_find(buf
, buflen
, hc_attrs
[i
].tag_id
,
440 &hc_attrs
[i
].pld_ofs
, &hc_attrs
[i
].pld_len
);
442 hc_attrs
[i
].pld_ofs
= hc_attrs
[i
].pld_len
= 0;
446 /* Account for skipped magic */
447 hc_attrs
[i
].pld_ofs
+= sizeof(magic
);
449 /* Special case RB_ID_WLAN_DATA to prep and create the binary attribute */
450 if ((RB_ID_WLAN_DATA
== hc_attrs
[i
].tag_id
) && hc_attrs
[i
].pld_len
) {
451 hc_wlandata_battr
.pld_ofs
= hc_attrs
[i
].pld_ofs
;
452 hc_wlandata_battr
.pld_len
= hc_attrs
[i
].pld_len
;
454 ret
= sysfs_create_bin_file(hc_kobj
, &hc_wlandata_battr
.battr
);
456 pr_err(RB_HC_PR_PFX
"Could not create %s sysfs entry (%d)\n",
457 hc_wlandata_battr
.battr
.attr
.name
, ret
);
459 /* All other tags are published via standard attributes */
461 ret
= sysfs_create_file(hc_kobj
, &hc_attrs
[i
].kattr
.attr
);
463 pr_err(RB_HC_PR_PFX
"Could not create %s sysfs entry (%d)\n",
464 hc_attrs
[i
].kattr
.attr
.name
, ret
);
468 pr_info("MikroTik RouterBOARD hardware configuration sysfs driver v" RB_HARDCONFIG_VER
"\n");
477 void __exit
rb_hardconfig_exit(void)
479 kobject_put(hc_kobj
);