2023-08-30 21:18:13 -06:00
|
|
|
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/Makefile b/target/linux/generic/files/drivers/platform/mikrotik/Makefile
|
|
|
|
index a232e1a9e8488..2e1ab3dcf44c0 100644
|
|
|
|
--- a/target/linux/generic/files/drivers/platform/mikrotik/Makefile
|
|
|
|
+++ b/target/linux/generic/files/drivers/platform/mikrotik/Makefile
|
2023-09-02 01:15:34 -06:00
|
|
|
@@ -1,4 +1,4 @@
|
|
|
|
#
|
2023-08-30 21:18:13 -06:00
|
|
|
# Makefile for MikroTik RouterBoard platform specific drivers
|
|
|
|
#
|
2023-09-02 01:15:34 -06:00
|
|
|
-obj-$(CONFIG_MIKROTIK_RB_SYSFS) += routerboot.o rb_hardconfig.o rb_softconfig.o
|
|
|
|
+obj-$(CONFIG_MIKROTIK_RB_SYSFS) += routerboot.o rb_hardconfig.o rb_softconfig.o rb_hardconfig_lz77.o
|
2023-08-30 21:18:13 -06:00
|
|
|
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c
|
|
|
|
index bd0469d5e8385..83ba74341c8f9 100644
|
|
|
|
--- a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c
|
|
|
|
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig.c
|
|
|
|
@@ -38,8 +38,9 @@
|
|
|
|
#include <linux/lzo.h>
|
|
|
|
|
|
|
|
#include "routerboot.h"
|
|
|
|
+#include "rb_hardconfig_lz77.h"
|
|
|
|
|
|
|
|
-#define RB_HARDCONFIG_VER "0.07"
|
|
|
|
+#define RB_HARDCONFIG_VER "0.08"
|
|
|
|
#define RB_HC_PR_PFX "[rb_hardconfig] "
|
|
|
|
|
|
|
|
/* ID values for hardware settings */
|
|
|
|
@@ -563,6 +564,63 @@ static int hc_wlan_data_unpack_lzor(const u16 tag_id, const u8 *inbuf, size_t in
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
+static int hc_wlan_data_unpack_lz77(const u16 tag_id, const u8 *inbuf, size_t inlen,
|
|
|
|
+ void *outbuf, size_t *outlen)
|
|
|
|
+{
|
|
|
|
+ u16 rle_ofs, rle_len;
|
|
|
|
+ u8 *tempbuf;
|
|
|
|
+ size_t templen;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* Temporary buffer same size as the outbuf */
|
|
|
|
+ tempbuf = kmalloc(*outlen, GFP_KERNEL);
|
|
|
|
+ if (!tempbuf)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ /* LZO-decompress lzo_len bytes of outbuf into the tempbuf */
|
|
|
|
+ ret = lz77_mikrotik_wlan_decompress(
|
|
|
|
+ (const unsigned char *)inbuf,
|
|
|
|
+ inlen,
|
|
|
|
+ (unsigned char *)tempbuf,
|
|
|
|
+ outlen);
|
|
|
|
+ if (ret < 0) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX "LZ77: LZ77 decompress fail\n");
|
|
|
|
+ goto lz77_fail;
|
|
|
|
+ }
|
|
|
|
+ templen = ret;
|
|
|
|
+
|
|
|
|
+ pr_debug(RB_HC_PR_PFX "LZ77: decompressed from %zu to %d\n",
|
|
|
|
+ inlen, templen);
|
|
|
|
+
|
|
|
|
+ /* skip DRE magic */
|
|
|
|
+ tempbuf += 4;
|
|
|
|
+ templen -= 4;
|
|
|
|
+
|
|
|
|
+ /* Past magic. Look for tag node */
|
|
|
|
+ ret = routerboot_tag_find(tempbuf, templen, tag_id, &rle_ofs, &rle_len);
|
|
|
|
+ if (ret) {
|
|
|
|
+ pr_debug(RB_HC_PR_PFX "LZ77: no RLE data for id 0x%04x\n", tag_id);
|
|
|
|
+ goto lz77_fail;
|
|
|
|
+ }
|
|
|
|
+ pr_debug(RB_HC_PR_PFX "LZ77: found RLE data for id 0x%04x\n", tag_id);
|
|
|
|
+
|
|
|
|
+ if (rle_len > templen) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX "LZ77: Invalid RLE data length\n");
|
|
|
|
+ ret = -EINVAL;
|
|
|
|
+ goto lz77_fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* RLE-decode tempbuf back into the outbuf */
|
|
|
|
+ ret = routerboot_rle_decode(tempbuf+rle_ofs, rle_len, outbuf, outlen);
|
|
|
|
+ if (ret)
|
|
|
|
+ pr_debug(RB_HC_PR_PFX "LZ77: RLE decoding error (%d)\n", ret);
|
|
|
|
+
|
|
|
|
+lz77_fail:
|
|
|
|
+ kfree(tempbuf);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen,
|
|
|
|
void *outbuf, size_t *outlen)
|
|
|
|
{
|
|
|
|
@@ -585,6 +643,13 @@ static int hc_wlan_data_unpack(const u16 tag_id, const size_t tofs, size_t tlen,
|
|
|
|
tlen -= sizeof(magic);
|
|
|
|
ret = hc_wlan_data_unpack_lzor(tag_id, lbuf, tlen, outbuf, outlen);
|
|
|
|
break;
|
|
|
|
+ case RB_MAGIC_LZ77:
|
|
|
|
+ /* Skip magic */
|
|
|
|
+ lbuf += sizeof(magic);
|
|
|
|
+ tlen -= sizeof(magic);
|
|
|
|
+ ret = hc_wlan_data_unpack_lz77(tag_id, lbuf, tlen, outbuf, outlen);
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
case RB_MAGIC_ERD:
|
|
|
|
/* Skip magic */
|
|
|
|
lbuf += sizeof(magic);
|
|
|
|
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig_lz77.c b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig_lz77.c
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000000000..8e8b7a1689c87
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig_lz77.c
|
2023-12-06 11:12:11 -07:00
|
|
|
@@ -0,0 +1,438 @@
|
2023-08-30 21:18:13 -06:00
|
|
|
+// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
+/*
|
|
|
|
+ * Copyright (C) 2023 John Thomson
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <linux/slab.h>
|
|
|
|
+#include <linux/string.h>
|
|
|
|
+#include <linux/errno.h>
|
|
|
|
+
|
|
|
|
+#include "rb_hardconfig_lz77.h"
|
|
|
|
+
|
|
|
|
+#define RB_HC_PR_PFX_LZ77 "[rb_hardconfig][lz77] "
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * lz77_mikrotik_wlan_get_bit
|
|
|
|
+ *
|
|
|
|
+ * @in: compressed data
|
|
|
|
+ * @in_offset_bit: bit offset to extract
|
|
|
|
+ */
|
|
|
|
+static unsigned int lz77_mikrotik_wlan_get_bit(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ unsigned int in_offset_bit)
|
|
|
|
+{
|
|
|
|
+ return ((in[in_offset_bit>>3] >> (in_offset_bit & 7)) & 0x1);
|
|
|
|
+
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * lz77_mikrotik_wlan_get_byte
|
|
|
|
+ *
|
|
|
|
+ * @in: compressed data
|
|
|
|
+ * @in_offset_bit: bit offset to extract byte
|
|
|
|
+ */
|
|
|
|
+static unsigned char lz77_mikrotik_wlan_get_byte(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ unsigned int in_offset_bit)
|
|
|
|
+{
|
|
|
|
+ unsigned char buf = 0;
|
|
|
|
+
|
|
|
|
+ /* built a byte from unaligned bits (reversed) */
|
|
|
|
+ int i; for (i = 0; i <= 7; ++i)
|
|
|
|
+ buf += ((in[(in_offset_bit+i)>>3] >> ((in_offset_bit+i) & 7)) & 0x1)<<(7-i);
|
|
|
|
+ return buf;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * lz77_mikrotik_wlan_decode_count - decode bits at given offset as a count
|
|
|
|
+ *
|
|
|
|
+ * @in: compressed data
|
|
|
|
+ * @in_offset_bit: bit offset where count starts
|
|
|
|
+ * @shift: left shift operand value of first count bit
|
|
|
|
+ * @count: initial count
|
|
|
|
+ * @bits_used: how many bits were consumed by this count
|
|
|
|
+ * @size: maximum bit count for this counter
|
|
|
|
+ *
|
|
|
|
+ * Returns the decoded count
|
|
|
|
+ */
|
|
|
|
+static int lz77_mikrotik_wlan_decode_count(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ unsigned int in_offset_bit,
|
|
|
|
+ unsigned int shift,
|
|
|
|
+ unsigned int count,
|
|
|
|
+ unsigned int *bits_used,
|
|
|
|
+ unsigned int size)
|
|
|
|
+{
|
|
|
|
+ unsigned int pos = in_offset_bit;
|
|
|
|
+ bool up = true;
|
|
|
|
+
|
|
|
|
+ size += pos;
|
|
|
|
+ *bits_used = 0;
|
|
|
|
+ pr_debug(RB_HC_PR_PFX_LZ77 "decode_count inbit: %i, start shift:%i, initial count:%i\n",
|
|
|
|
+ in_offset_bit, shift, count);
|
|
|
|
+
|
|
|
|
+ /* maybe could use find_first_zero_bit to skip count up,
|
|
|
|
+ * and extract for the count down,
|
|
|
|
+ * but would need to mask the bits from and to the byte align boundary
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+ while (true) {
|
|
|
|
+ if (pos >= size) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "max bit index reached before count completed\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* if the bit value at offset is set */
|
|
|
|
+ if (lz77_mikrotik_wlan_get_bit(in, pos)) {
|
|
|
|
+ count += (1 << shift);
|
|
|
|
+
|
|
|
|
+ /* shift increases until we find an unsed bit */
|
|
|
|
+ } else if (up) {
|
|
|
|
+ up = false;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (up) {
|
|
|
|
+ ++shift;
|
|
|
|
+ } else {
|
|
|
|
+ if (!shift) {
|
|
|
|
+ *bits_used = pos - in_offset_bit + 1;
|
|
|
|
+ return count;
|
|
|
|
+ }
|
|
|
|
+ --shift;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ++pos;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return -1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+enum lz77_mikrotik_instruction {
|
|
|
|
+ INSTR_ERROR = -1,
|
|
|
|
+ INSTR_LITERAL_BYTE = 0,
|
|
|
|
+ /* a (non aligned) byte follows this instruction,
|
|
|
|
+ * which is directly copied into output
|
|
|
|
+ */
|
|
|
|
+ INSTR_PREVIOUS_OFFSET = 1,
|
|
|
|
+ /* this group is a match, with a bytes length defined by
|
|
|
|
+ * following counter bits, starting at bitshift 0,
|
|
|
|
+ * less the built-in count of 1
|
|
|
|
+ * using the previous offset as source
|
|
|
|
+ */
|
|
|
|
+ INSTR_LONG = 2
|
|
|
|
+ /* this group has two counters,
|
|
|
|
+ * the first counter starts at bitshift 4,
|
|
|
|
+ * if this counter == 0, this is a non-matching group
|
|
|
|
+ * the second counter (bytes length) starts at bitshift 4,
|
|
|
|
+ * less the built-in count of 11+1.
|
|
|
|
+ * The final match group has this count 0,
|
|
|
|
+ * and (at least 1) 0 bits pad to byte-align
|
|
|
|
+ *
|
|
|
|
+ * if this counter > 0, this is a matching group
|
|
|
|
+ * this first count is the match offset (in bytes)
|
|
|
|
+ * the second count is the match length (in bytes),
|
|
|
|
+ * less the built-in count of 2
|
|
|
|
+ * these groups can source bytes that are part of this group
|
|
|
|
+ */
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * lz77_mikrotik_wlan_decode_instruction
|
|
|
|
+ *
|
|
|
|
+ * @in: compressed data
|
|
|
|
+ * @in_offset_bit: bit offset where instruction starts
|
|
|
|
+ * @bits_used: how many bits were consumed by this count
|
|
|
|
+ *
|
|
|
|
+ * Returns the decoded instruction
|
|
|
|
+ */
|
|
|
|
+static enum lz77_mikrotik_instruction lz77_mikrotik_wlan_decode_instruction(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ unsigned int in_offset_bit,
|
|
|
|
+ unsigned int *bits_used)
|
|
|
|
+{
|
|
|
|
+ if (lz77_mikrotik_wlan_get_bit(in, in_offset_bit)) {
|
|
|
|
+ *bits_used = 2;
|
|
|
|
+ if (lz77_mikrotik_wlan_get_bit(in, ++in_offset_bit)) {
|
|
|
|
+ return INSTR_LONG;
|
|
|
|
+ } else {
|
|
|
|
+ return INSTR_PREVIOUS_OFFSET;
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ *bits_used = 1;
|
|
|
|
+ return INSTR_LITERAL_BYTE;
|
|
|
|
+ }
|
|
|
|
+ return INSTR_ERROR;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+struct lz77_mk_instr_opcodes {
|
|
|
|
+ /* group instruction */
|
|
|
|
+ enum lz77_mikrotik_instruction instruction;
|
|
|
|
+ /* if >0, a match group,
|
|
|
|
+ * which starts at byte output_position - 1*offset
|
|
|
|
+ */
|
|
|
|
+ unsigned int offset;
|
|
|
|
+ /* how long the match group is,
|
|
|
|
+ * or how long the (following counter) non-match group is
|
|
|
|
+ */
|
|
|
|
+ unsigned int length;
|
|
|
|
+ /* how many bits were used for this instruction + op code(s) */
|
|
|
|
+ unsigned int bits_used;
|
|
|
|
+ /* input char */
|
|
|
|
+ unsigned char *in;
|
|
|
|
+ /* offset where this instruction started */
|
|
|
|
+ unsigned int in_pos;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * lz77_mikrotik_wlan_decode_instruction_operators
|
|
|
|
+ *
|
|
|
|
+ * @in: compressed data
|
|
|
|
+ * @in_offset_bit: bit offset where instruction starts
|
|
|
|
+ * @previous_offset: last used match offset
|
|
|
|
+ * @opcode: struct to hold instruction & operators
|
|
|
|
+ *
|
|
|
|
+ * Returns error code
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+static int lz77_mikrotik_wlan_decode_instruction_operators(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ unsigned int in_offset_bit,
|
|
|
|
+ unsigned int previous_offset,
|
|
|
|
+ struct lz77_mk_instr_opcodes *opcode)
|
|
|
|
+{
|
|
|
|
+ enum lz77_mikrotik_instruction instruction;
|
|
|
|
+ unsigned int bit_count = 0;
|
|
|
|
+ unsigned int bits_used = 0;
|
|
|
|
+ int offset = 0;
|
|
|
|
+ int length = 0;
|
|
|
|
+
|
|
|
|
+ instruction = lz77_mikrotik_wlan_decode_instruction(
|
|
|
|
+ in, in_offset_bit, &bit_count);
|
|
|
|
+
|
|
|
|
+ /* skip bits used by instruction */
|
|
|
|
+ bits_used += bit_count;
|
|
|
|
+
|
|
|
|
+ switch (instruction) {
|
|
|
|
+ case INSTR_LITERAL_BYTE:
|
|
|
|
+ /* non-matching char */
|
|
|
|
+ offset = 0;
|
|
|
|
+ length = 1;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case INSTR_PREVIOUS_OFFSET:
|
|
|
|
+ /* matching group uses previous offset */
|
|
|
|
+ offset = previous_offset;
|
|
|
|
+
|
|
|
|
+ length = lz77_mikrotik_wlan_decode_count(
|
|
|
|
+ in,
|
|
|
|
+ in_offset_bit + bits_used,
|
|
|
|
+ 0, 1, &bit_count,
|
|
|
|
+ LZ77_MK_MAX_COUNT_BIT_LEN);
|
|
|
|
+ if (length < 0)
|
|
|
|
+ return -1;
|
|
|
|
+ /* skip bits used by count */
|
|
|
|
+ bits_used += bit_count;
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case INSTR_LONG:
|
|
|
|
+ offset = lz77_mikrotik_wlan_decode_count(
|
|
|
|
+ in,
|
|
|
|
+ in_offset_bit + bits_used,
|
|
|
|
+ 4, 0, &bit_count,
|
|
|
|
+ LZ77_MK_MAX_COUNT_BIT_LEN);
|
|
|
|
+ if (offset < 0)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ /* skip bits used by offset count */
|
|
|
|
+ bits_used += bit_count;
|
|
|
|
+
|
|
|
|
+ if (offset == 0) {
|
|
|
|
+ /* non-matching long group */
|
|
|
|
+ length = lz77_mikrotik_wlan_decode_count(
|
|
|
|
+ in,
|
|
|
|
+ in_offset_bit + bits_used,
|
|
|
|
+ 4, 12, &bit_count,
|
|
|
|
+ LZ77_MK_MAX_COUNT_BIT_LEN);
|
|
|
|
+ if (length < 0)
|
|
|
|
+ return -1;
|
|
|
|
+ /* skip bits used by length count */
|
|
|
|
+ bits_used += bit_count;
|
|
|
|
+ } else {
|
|
|
|
+ /* matching group */
|
|
|
|
+ length = lz77_mikrotik_wlan_decode_count(
|
|
|
|
+ in,
|
|
|
|
+ in_offset_bit + bits_used,
|
|
|
|
+ 0, 2, &bit_count,
|
|
|
|
+ LZ77_MK_MAX_COUNT_BIT_LEN);
|
|
|
|
+ if (length < 0)
|
|
|
|
+ return -1;
|
|
|
|
+ /* skip bits used by length count */
|
|
|
|
+ bits_used += bit_count;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case INSTR_ERROR:
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ opcode->instruction = instruction;
|
|
|
|
+ opcode->offset = offset;
|
|
|
|
+ opcode->length = length;
|
|
|
|
+ opcode->bits_used = bits_used;
|
|
|
|
+ opcode->in = (unsigned char *) in;
|
|
|
|
+ opcode->in_pos = in_offset_bit;
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * lz77_mikrotik_wlan_decompress
|
|
|
|
+ *
|
|
|
|
+ * @in: compressed data ptr
|
|
|
|
+ * @in_len: length of compressed data
|
|
|
|
+ * @out: buffer ptr to decompress into
|
|
|
|
+ * @out_len: length of decompressed buffer
|
|
|
|
+ *
|
|
|
|
+ * Returns length of decompressed data
|
|
|
|
+ */
|
|
|
|
+int lz77_mikrotik_wlan_decompress(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ size_t in_len,
|
|
|
|
+ unsigned char *out,
|
|
|
|
+ size_t *out_len)
|
|
|
|
+{
|
|
|
|
+ unsigned char *output_ptr;
|
|
|
|
+ unsigned int input_bit = 0;
|
|
|
|
+ unsigned char * const output_end = out + *out_len;
|
|
|
|
+ struct lz77_mk_instr_opcodes *opcode;
|
|
|
|
+ unsigned int match_offset = 0;
|
|
|
|
+ int rc = 0;
|
|
|
|
+ unsigned int match_length, partial_count;
|
|
|
|
+
|
|
|
|
+ output_ptr = out;
|
|
|
|
+
|
|
|
|
+ if (in_len > LZ77_MK_MAX_ENCODED ||
|
|
|
|
+ (in_len * 8) > UINT_MAX) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "input longer than expected\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ opcode = kmalloc(sizeof(struct lz77_mk_instr_opcodes), GFP_KERNEL);
|
|
|
|
+ if (!opcode)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ while (true) {
|
|
|
|
+ if (output_ptr > output_end) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "output overrun\n");
|
|
|
|
+ goto free_lz77_struct;
|
|
|
|
+ }
|
|
|
|
+ if (input_bit > in_len*8) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "input overrun\n");
|
|
|
|
+ goto free_lz77_struct;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rc = lz77_mikrotik_wlan_decode_instruction_operators(
|
|
|
|
+ in, input_bit, match_offset, opcode);
|
|
|
|
+ if (rc < 0) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "instruction operands decode error\n");
|
|
|
|
+ goto free_lz77_struct;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pr_debug(RB_HC_PR_PFX_LZ77 "inbit:0x%x->outbyte:0x%x",
|
|
|
|
+ input_bit, output_ptr - out);
|
|
|
|
+
|
|
|
|
+ input_bit += opcode->bits_used;
|
|
|
|
+ switch (opcode->instruction) {
|
|
|
|
+ case INSTR_LITERAL_BYTE:
|
|
|
|
+ pr_debug(" short");
|
|
|
|
+ fallthrough;
|
|
|
|
+ case INSTR_LONG:
|
|
|
|
+ if (opcode->offset == 0) {
|
2023-12-06 11:12:11 -07:00
|
|
|
+ unsigned int i;
|
2023-08-30 21:18:13 -06:00
|
|
|
+ /* this is a non-matching group */
|
|
|
|
+ pr_debug(" non-match, len: 0x%x\n",
|
|
|
|
+ opcode->length);
|
|
|
|
+ /* test end marker */
|
|
|
|
+ if (opcode->length == 0xc &&
|
|
|
|
+ ((input_bit + opcode->length*8) > in_len))
|
|
|
|
+ {
|
|
|
|
+
|
|
|
|
+ if (!(*(unsigned int *)out == LZ77_MK_EXPECTED_OUT)) {
|
|
|
|
+ pr_debug(RB_HC_PR_PFX_LZ77 "lz77 decompressed from %zu to %d\n",
|
|
|
|
+ in_len, output_ptr - out);
|
|
|
|
+ return (unsigned int)(output_ptr - out);
|
|
|
|
+ } else {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "lz77 decompressed: unexpected output\n");
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
2023-12-06 11:12:11 -07:00
|
|
|
+ for (i = opcode->length; i > 0; --i) {
|
2023-08-30 21:18:13 -06:00
|
|
|
+ *output_ptr = lz77_mikrotik_wlan_get_byte(in, input_bit);
|
|
|
|
+ ++output_ptr;
|
|
|
|
+ input_bit += 8;
|
|
|
|
+ }
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ match_offset = opcode->offset;
|
|
|
|
+ fallthrough;
|
|
|
|
+ case INSTR_PREVIOUS_OFFSET:
|
|
|
|
+ match_length = opcode->length;
|
|
|
|
+ partial_count = 0;
|
|
|
|
+
|
|
|
|
+ pr_debug(" match, offset: 0x%x, len: 0x%x",
|
|
|
|
+ opcode->offset, match_length);
|
|
|
|
+
|
|
|
|
+ if (opcode->offset == 0) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "match group missing opcode->offset\n");
|
|
|
|
+ goto free_lz77_struct;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* overflow */
|
|
|
|
+ if ((output_ptr + match_length) > output_end) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "match group output overflow\n");
|
|
|
|
+ goto free_lz77_struct;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* underflow */
|
|
|
|
+ if ((output_ptr - opcode->offset) < out) {
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "match group offset underflow\n");
|
|
|
|
+ goto free_lz77_struct;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ while (opcode->offset < match_length) {
|
|
|
|
+ ++partial_count;
|
|
|
|
+ memcpy(output_ptr,
|
|
|
|
+ output_ptr - opcode->offset,
|
|
|
|
+ opcode->offset);
|
|
|
|
+ output_ptr += opcode->offset;
|
|
|
|
+ match_length -= opcode->offset;
|
|
|
|
+ }
|
|
|
|
+ memcpy(output_ptr,
|
|
|
|
+ output_ptr - opcode->offset,
|
|
|
|
+ match_length);
|
|
|
|
+ output_ptr += match_length;
|
|
|
|
+ if (partial_count) {
|
|
|
|
+ pr_debug(" (%d partial memcpy)\n", partial_count);
|
|
|
|
+ } else {
|
|
|
|
+ pr_debug("\n");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ break;
|
|
|
|
+
|
|
|
|
+ case INSTR_ERROR:
|
|
|
|
+ return -1;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ pr_err(RB_HC_PR_PFX_LZ77 "decode loop broken\n");
|
|
|
|
+
|
|
|
|
+free_lz77_struct:
|
|
|
|
+ kfree(opcode);
|
|
|
|
+
|
|
|
|
+ return -1;
|
|
|
|
+}
|
|
|
|
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig_lz77.h b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig_lz77.h
|
|
|
|
new file mode 100644
|
|
|
|
index 0000000000000..50d19f54b3618
|
|
|
|
--- /dev/null
|
|
|
|
+++ b/target/linux/generic/files/drivers/platform/mikrotik/rb_hardconfig_lz77.h
|
2023-09-02 01:15:34 -06:00
|
|
|
@@ -0,0 +1,25 @@
|
2023-08-30 21:18:13 -06:00
|
|
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
+/*
|
|
|
|
+ * Copyright (C) 2023 John Thomson
|
|
|
|
+ */
|
|
|
|
+
|
|
|
|
+#include <linux/errno.h>
|
|
|
|
+
|
|
|
|
+#define LZ77_MK_MAX_ENCODED 0x1000
|
|
|
|
+#define LZ77_MK_MAX_DECODED 0x40000
|
|
|
|
+
|
|
|
|
+/* examples have all decompressed to start with DRE\x00 */
|
|
|
|
+#define LZ77_MK_EXPECTED_OUT 0x44524500
|
|
|
|
+
|
|
|
|
+/* the look behind window
|
|
|
|
+ * unknown, for long instruction match offsets up to
|
|
|
|
+ * 6449 have been seen (would need 21 counter bits: 4 to 12 + 11 to 0)
|
|
|
|
+ * conservative value here: 27 provides offset up to 0x8000 bytes
|
|
|
|
+ */
|
|
|
|
+#define LZ77_MK_MAX_COUNT_BIT_LEN 27
|
|
|
|
+
|
|
|
|
+int lz77_mikrotik_wlan_decompress(
|
|
|
|
+ const unsigned char *in,
|
|
|
|
+ size_t in_len,
|
|
|
|
+ unsigned char *out,
|
|
|
|
+ size_t *out_len);
|
|
|
|
diff --git a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h
|
|
|
|
index e858a524af43f..91693d8985226 100644
|
|
|
|
--- a/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h
|
|
|
|
+++ b/target/linux/generic/files/drivers/platform/mikrotik/routerboot.h
|
|
|
|
@@ -16,6 +16,7 @@
|
|
|
|
#define RB_MAGIC_SOFT (('S') | ('o' << 8) | ('f' << 16) | ('t' << 24))
|
|
|
|
#define RB_MAGIC_LZOR (('L') | ('Z' << 8) | ('O' << 16) | ('R' << 24))
|
|
|
|
#define RB_MAGIC_ERD (('E' << 16) | ('R' << 8) | ('D'))
|
|
|
|
+#define RB_MAGIC_LZ77 (('7') | ('7' << 8) | ('Z' << 16) | ('L' << 24))
|
|
|
|
|
|
|
|
#define RB_ART_SIZE 0x10000
|
|
|
|
|