Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP][RFT] generic: platform/mikrotik: add wlan lz77 decompress #5

Merged
merged 1 commit into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions target/linux/ath79/mikrotik/config-default
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ CONFIG_MFD_CORE=y
CONFIG_MFD_RB4XX_CPLD=y
CONFIG_MIKROTIK=y
CONFIG_MIKROTIK_RB_SYSFS=y
CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77=y
CONFIG_MTD_NAND_AR934X=y
CONFIG_MTD_NAND_CORE=y
CONFIG_MTD_NAND_ECC=y
Expand Down
7 changes: 7 additions & 0 deletions target/linux/generic/files/drivers/platform/mikrotik/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,11 @@ config NVMEM_LAYOUT_MIKROTIK
help
This driver exposes MikroTik hard_config via NVMEM layout.

config MIKROTIK_WLAN_DECOMPRESS_LZ77
tristate "Mikrotik factory Wi-Fi caldata LZ77 decompression support"
depends on MIKROTIK_RB_SYSFS
help
Allow Mikrotik LZ77 factory flashed Wi-Fi calibration data to be
decompressed

endif # MIKROTIK
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
#
obj-$(CONFIG_MIKROTIK_RB_SYSFS) += routerboot.o rb_hardconfig.o rb_softconfig.o
obj-$(CONFIG_NVMEM_LAYOUT_MIKROTIK) += rb_nvmem.o
obj-$(CONFIG_MIKROTIK_WLAN_DECOMPRESS_LZ77) += rb_lz77.o
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@

#include "rb_hardconfig.h"
#include "routerboot.h"
#include "rb_lz77.h"

#define RB_HARDCONFIG_VER "0.07"
#define RB_HARDCONFIG_VER "0.08"
#define RB_HC_PR_PFX "[rb_hardconfig] "

/* Bit definitions for hardware options */
Expand Down Expand Up @@ -465,47 +466,75 @@ static int hc_wlan_data_unpack_erd(const u16 tag_id, const u8 *inbuf, size_t inl
/*
* If the RB_ID_WLAN_DATA payload starts with RB_MAGIC_LZOR, then past
* that magic number is a payload that must be appended to the hc_lzor_prefix,
* the resulting blob is LZO-compressed. In the LZO decompression result,
* the resulting blob is LZO-compressed.
* If payload starts with RB_MAGIC_LZ77, a separate (bit level LZ77)
* decompression function needs to be used. In the decompressed result,
* the RB_MAGIC_ERD magic number (aligned) must be located. Following that
* magic, there is one or more routerboot tag node(s) locating the RLE-encoded
* calibration data payload.
*/
static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t inlen,
void *outbuf, size_t *outlen)
static int hc_wlan_data_unpack_lzor_lz77(const u16 tag_id, const u8 *inbuf, size_t inlen,
void *outbuf, size_t *outlen, u32 magic)
{
u16 rle_ofs, rle_len;
const u32 *needle;
u8 *tempbuf;
size_t templen, lzo_len;
int ret;

lzo_len = inlen + sizeof(hc_lzor_prefix);
if (lzo_len > *outlen)
return -EFBIG;
const char lzor[] = "LZOR";
const char lz77[] = "LZ77";
const char *lz_type;

/* Temporary buffer same size as the outbuf */
templen = *outlen;
tempbuf = kmalloc(templen, GFP_KERNEL);
if (!tempbuf)
return -ENOMEM;

/* Concatenate into the outbuf */
memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);
lzo_len = inlen;
if (magic == RB_MAGIC_LZOR)
lzo_len += sizeof(hc_lzor_prefix);
if (lzo_len > *outlen)
return -EFBIG;

/* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
if (ret) {
if (LZO_E_INPUT_NOT_CONSUMED == ret) {
/*
* The tag length is always aligned thus the LZO payload may be padded,
* which can trigger a spurious error which we ignore here.
*/
pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
} else {
pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
switch (magic) {
case RB_MAGIC_LZOR:
lz_type = lzor;

/* Concatenate into the outbuf */
memcpy(outbuf, hc_lzor_prefix, sizeof(hc_lzor_prefix));
memcpy(outbuf + sizeof(hc_lzor_prefix), inbuf, inlen);

/* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
ret = lzo1x_decompress_safe(outbuf, lzo_len, tempbuf, &templen);
if (ret) {
if (LZO_E_INPUT_NOT_CONSUMED == ret) {
/*
* The tag length is always aligned thus the LZO payload may be padded,
* which can trigger a spurious error which we ignore here.
*/
pr_debug(RB_HC_PR_PFX "LZOR: LZO EOF before buffer end - this may be harmless\n");
} else {
pr_debug(RB_HC_PR_PFX "LZOR: LZO decompression error (%d)\n", ret);
goto fail;
}
}
break;
case RB_MAGIC_LZ77:
lz_type = lz77;
/* LZO-decompress lzo_len bytes of inbuf into the tempbuf */
ret = rb_lz77_decompress(inbuf, inlen, tempbuf, &templen);
if (ret) {
pr_err(RB_HC_PR_PFX "LZ77: LZ77 decompress error %d\n", ret);
goto fail;
}

pr_debug(RB_HC_PR_PFX "LZ77: decompressed from %zu to %zu\n",
inlen, templen);
break;
default:
return -EINVAL;
break;
}

/*
Expand All @@ -516,7 +545,7 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
needle = (const u32 *)tempbuf;
while (RB_MAGIC_ERD != *needle++) {
if ((u8 *)needle >= tempbuf+templen) {
pr_debug(RB_HC_PR_PFX "LZOR: ERD magic not found\n");
pr_warn(RB_HC_PR_PFX "%s: ERD magic not found. Decompressed first word: 0x%08x\n", lz_type, *(u32 *)tempbuf);
ret = -ENODATA;
goto fail;
}
Expand All @@ -526,20 +555,20 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
/* Past magic. Look for tag node */
ret = routerboot_tag_find((u8 *)needle, templen, tag_id, &rle_ofs, &rle_len);
if (ret) {
pr_debug(RB_HC_PR_PFX "LZOR: no RLE data for id 0x%04x\n", tag_id);
pr_debug(RB_HC_PR_PFX "%s: no RLE data for id 0x%04x\n", lz_type, tag_id);
goto fail;
}

if (rle_len > templen) {
pr_debug(RB_HC_PR_PFX "LZOR: Invalid RLE data length\n");
pr_debug(RB_HC_PR_PFX "%s: Invalid RLE data length\n", lz_type);
ret = -EINVAL;
goto fail;
}

/* RLE-decode tempbuf from needle back into the outbuf */
ret = routerboot_rle_decode((u8 *)needle+rle_ofs, rle_len, outbuf, outlen);
if (ret)
pr_debug(RB_HC_PR_PFX "LZOR: RLE decoding error (%d)\n", ret);
pr_debug(RB_HC_PR_PFX "%s: RLE decoding error (%d)\n", lz_type, ret);

fail:
kfree(tempbuf);
Expand All @@ -562,11 +591,18 @@ static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen,

ret = -ENODATA;
switch (magic) {
case RB_MAGIC_LZ77:
/* no known instances of lz77 without 8001/8201 data, skip SOLO */
if (tag_id == RB_WLAN_ERD_ID_SOLO) {
pr_debug(RB_HC_PR_PFX "skipped LZ77 decompress in search for SOLO tag\n");
break;
}
fallthrough;
case RB_MAGIC_LZOR:
/* Skip magic */
lbuf += sizeof(magic);
tlen -= sizeof(magic);
ret = hc_wlan_data_unpack_lzor(tag_id, lbuf, tlen, outbuf, outlen);
ret = hc_wlan_data_unpack_lzor_lz77(tag_id, lbuf, tlen, outbuf, outlen, magic);
break;
case RB_MAGIC_ERD:
/* Skip magic */
Expand Down
Loading