^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Wrapper for decompressing LZ4-compressed kernel, initramfs, and initrd
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2013, LG Electronics, Kyungsik Lee <kyungsik.lee@lge.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #ifdef STATIC
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #define PREBOOT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include "lz4/lz4_decompress.c"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/decompress/unlz4.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/types.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/lz4.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/decompress/mm.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include <linux/compiler.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include <asm/unaligned.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * Note: Uncompressed chunk size is used in the compressor side
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * (userspace side for compression).
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * It is hardcoded because there is not proper way to extract it
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * from the binary stream which is generated by the preliminary
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * version of LZ4 tool so far.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define ARCHIVE_MAGICNUMBER 0x184C2102
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) STATIC inline int INIT unlz4(u8 *input, long in_len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) long (*fill)(void *, unsigned long),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) long (*flush)(void *, unsigned long),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) u8 *output, long *posp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) void (*error) (char *x))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) int ret = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) size_t chunksize = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) size_t uncomp_chunksize = LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) u8 *inp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) u8 *inp_start;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) u8 *outp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) long size = in_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) #ifdef PREBOOT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) size_t out_len = get_unaligned_le32(input + in_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) size_t dest_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) if (output) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) outp = output;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) } else if (!flush) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) error("NULL output pointer and no flush function provided");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) goto exit_0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) outp = large_malloc(uncomp_chunksize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) if (!outp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) error("Could not allocate output buffer");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) goto exit_0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) if (input && fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) error("Both input pointer and fill function provided,");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) goto exit_1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) } else if (input) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) inp = input;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) } else if (!fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) error("NULL input pointer and missing fill function");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) goto exit_1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) inp = large_malloc(LZ4_compressBound(uncomp_chunksize));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) if (!inp) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) error("Could not allocate input buffer");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) goto exit_1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) inp_start = inp;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) if (posp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) *posp = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) size = fill(inp, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) if (size < 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) error("data corrupted");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) chunksize = get_unaligned_le32(inp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) if (chunksize == ARCHIVE_MAGICNUMBER) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) if (!fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) inp += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) size -= 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) error("invalid header");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) if (posp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) *posp += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) for (;;) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) if (fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) size = fill(inp, 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) if (size == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) if (size < 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112) error("data corrupted");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) } else if (size < 4) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) /* empty or end-of-file */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) goto exit_3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) chunksize = get_unaligned_le32(inp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) if (chunksize == ARCHIVE_MAGICNUMBER) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) if (!fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) inp += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) size -= 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) if (posp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) *posp += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) if (!fill && chunksize == 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132) /* empty or end-of-file */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) goto exit_3;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) if (posp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) *posp += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) if (!fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) inp += 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141) size -= 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) if (chunksize > LZ4_compressBound(uncomp_chunksize)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) error("chunk length is longer than allocated");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) size = fill(inp, chunksize);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) if (size < chunksize) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) error("data corrupted");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 152) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 153) #ifdef PREBOOT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 154) if (out_len >= uncomp_chunksize) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 155) dest_len = uncomp_chunksize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 156) out_len -= dest_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 157) } else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 158) dest_len = out_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 159)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 160) ret = LZ4_decompress_fast(inp, outp, dest_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 161) chunksize = ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 162) #else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 163) dest_len = uncomp_chunksize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 164)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 165) ret = LZ4_decompress_safe(inp, outp, chunksize, dest_len);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 166) dest_len = ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 167) #endif
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 168) if (ret < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 169) error("Decoding failed");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 170) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 171) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 172)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 173) ret = -1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 174) if (flush && flush(outp, dest_len) != dest_len)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 175) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 176) if (output)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 177) outp += dest_len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 178) if (posp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 179) *posp += chunksize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 180)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 181) if (!fill) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 182) size -= chunksize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 183)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 184) if (size == 0)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 185) break;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 186) else if (size < 0) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 187) error("data corrupted");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 188) goto exit_2;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 189) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 190) inp += chunksize;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 191) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 192) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 193)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 194) exit_3:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 195) ret = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 196) exit_2:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 197) if (!input)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 198) large_free(inp_start);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 199) exit_1:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 200) if (!output)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 201) large_free(outp);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 202) exit_0:
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 203) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 204) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 205)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 206) #ifdef PREBOOT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 207) STATIC int INIT __decompress(unsigned char *buf, long in_len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 208) long (*fill)(void*, unsigned long),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 209) long (*flush)(void*, unsigned long),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 210) unsigned char *output, long out_len,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 211) long *posp,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 212) void (*error)(char *x)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 213) )
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 214) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 215) return unlz4(buf, in_len - 4, fill, flush, output, posp, error);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 216) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 217) #endif