|  | // SPDX-License-Identifier: GPL 2.0+ OR BSD-3-Clause | 
|  | /* | 
|  | * Copyright 2015 Google Inc. | 
|  | */ | 
|  |  | 
|  | #include <common.h> | 
|  | #include <compiler.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  | #include <errno.h> | 
|  |  | 
|  | #include "lz4_wrapper.h" | 
|  |  | 
|  | #define LZ4F_MAGIC 0x184D2204 | 
|  |  | 
|  | #define LZ4_MAX_BLOCK_SIZE_64K 4 | 
|  | #define LZ4_MAX_BLOCK_SIZE_256K 5 | 
|  | #define LZ4_MAX_BLOCK_SIZE_1M 6 | 
|  | #define LZ4_MAX_BLOCK_SIZE_4M 7 | 
|  |  | 
|  | struct lz4_block_header { | 
|  | union { | 
|  | u32 raw; | 
|  | struct { | 
|  | u32 size : 31; | 
|  | u32 not_compressed : 1; | 
|  | }; | 
|  | }; | 
|  | /* + size bytes of data */ | 
|  | /* + u32 block_checksum iff has_block_checksum is set */ | 
|  | } __packed; | 
|  |  | 
|  | static u16 LZ4_readLE16(const void *src) | 
|  | { | 
|  | return le16_to_cpu(*(u16 *)src); | 
|  | } | 
|  | static void LZ4_copy4(void *dst, const void *src) | 
|  | { | 
|  | *(u32 *)dst = *(u32 *)src; | 
|  | } | 
|  | static void LZ4_copy8(void *dst, const void *src) | 
|  | { | 
|  | *(u64 *)dst = *(u64 *)src; | 
|  | } | 
|  |  | 
|  | /* Unaltered (except removing unrelated code) from github.com/Cyan4973/lz4. */ | 
|  | #include "lz4.c" /* #include for inlining, do not link! */ | 
|  |  | 
|  | bool is_data_lz4_compressed(const void *src, size_t srcn) | 
|  | { | 
|  | if (srcn < sizeof(struct lz4_frame_header)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | struct lz4_frame_header h; | 
|  | memcpy(&h, src, sizeof(h)); | 
|  | return le32_to_cpu(h.magic) == LZ4F_MAGIC; | 
|  | } | 
|  |  | 
|  | int check_and_extract_lz4_frame_header(const void *src, size_t srcn, | 
|  | struct lz4_frame_header *out, | 
|  | size_t *data_offset) | 
|  | { | 
|  | const void *in = src; | 
|  | const struct lz4_frame_header *h = in; | 
|  |  | 
|  | if (srcn < sizeof(*h) + sizeof(u64) + sizeof(u8)) | 
|  | return -EINVAL; /* input overrun */ | 
|  |  | 
|  | /* We assume there's always only a single, standard frame. */ | 
|  | if (le32_to_cpu(h->magic) != LZ4F_MAGIC || h->version != 1) { | 
|  | fprintf(stderr, "invalid magic %x, %d\n", | 
|  | le32_to_cpu(h->magic) != LZ4F_MAGIC, h->version); | 
|  | return -EPROTONOSUPPORT; /* unknown format */ | 
|  | } | 
|  | if (h->reserved0 || h->reserved1 || h->reserved2) | 
|  | return -EINVAL; /* reserved must be zero */ | 
|  | if (!h->independent_blocks) { | 
|  | fprintf(stderr, "no support for independent block\n"); | 
|  | return -EPROTONOSUPPORT; /* we can't support this yet */ | 
|  | } | 
|  |  | 
|  | in += sizeof(*h); | 
|  | if (h->has_content_size) | 
|  | in += sizeof(u64); | 
|  | in += sizeof(u8); | 
|  | *data_offset = in - src; | 
|  | *out = *h; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /** | 
|  | * Decompress one block of data from lz4 data. | 
|  | * | 
|  | * @param src	The lz4 compressed data. | 
|  | * @param srcn	Size of |src|. | 
|  | * @param dst	Output pointer for storing the decompress block. | 
|  | * @param dstn	Size of |dst|. | 
|  | * @param block_size	Output pointer for size of current compressed block. | 
|  | * @param decompressed_size Output pointer for the amount of data decompressed. | 
|  | */ | 
|  | int decompress_block(const void *src, size_t srcn, void *dst, int dstn, | 
|  | size_t *block_size, size_t *decompressed_size) | 
|  | { | 
|  | const void *in = src; | 
|  | void *out = dst; | 
|  | const void *end = out + dstn; | 
|  | *decompressed_size = 0; | 
|  | struct lz4_block_header b; | 
|  | b.raw = le32_to_cpu(*(u32 *)in); | 
|  | *block_size = sizeof(struct lz4_block_header) + b.size; | 
|  | in += sizeof(struct lz4_block_header); | 
|  | if (in - src + b.size > srcn) { | 
|  | fprintf(stderr, "input overrun\n"); | 
|  | return -EINVAL; /* input overrun */ | 
|  | } | 
|  |  | 
|  | if (!b.size) { | 
|  | return 0; /* decompression successful */ | 
|  | } | 
|  |  | 
|  | if (b.not_compressed) { | 
|  | size_t size = min((ptrdiff_t)b.size, end - out); | 
|  | memcpy(out, in, size); | 
|  | out += size; | 
|  | if (size < b.size) { | 
|  | fprintf(stderr, "output overrun\n"); | 
|  | return -ENOBUFS; /* output overrun */ | 
|  | } | 
|  | } else { | 
|  | /* constant folding essential, do not touch params! */ | 
|  | int ret = LZ4_decompress_generic((const char* const)in, out, b.size, end - out, | 
|  | endOnInputSize, full, 0, | 
|  | noDict, out, NULL, 0); | 
|  | if (ret < 0) { | 
|  | fprintf(stderr, "LZ4_decompress_generic error %d\n", | 
|  | ret); | 
|  | return -EPROTO; /* decompression error */ | 
|  | } | 
|  | out += ret; | 
|  | } | 
|  |  | 
|  | *decompressed_size = out - dst; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int ulz4fn(const void *src, size_t srcn, void *dst, size_t *dstn) | 
|  | { | 
|  | const void *src_end = src + srcn; | 
|  | const void *dst_end = dst + *dstn; | 
|  | struct lz4_frame_header h; | 
|  | size_t data_offset = 0; | 
|  | int ret = | 
|  | check_and_extract_lz4_frame_header(src, srcn, &h, &data_offset); | 
|  | if (ret) { | 
|  | return ret; | 
|  | } | 
|  | const void *data = (uint8_t *)src + data_offset; | 
|  | void *out = dst; | 
|  | size_t decompressed_size = 0, block_size; | 
|  | do { | 
|  | if ((ret = decompress_block(data, src_end - data, out, | 
|  | dst_end - out, &block_size, | 
|  | &decompressed_size))) { | 
|  | return ret; | 
|  | } | 
|  | out += decompressed_size; | 
|  | data += block_size + (h.has_block_checksum ? sizeof(u32) : 0); | 
|  | } while (decompressed_size); | 
|  |  | 
|  | *dstn = out - dst; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int get_lz4_max_block_size(const struct lz4_frame_header *h, size_t *out) | 
|  | { | 
|  | switch (h->max_block_size) { | 
|  | case LZ4_MAX_BLOCK_SIZE_64K: | 
|  | *out = 64 * 1024; | 
|  | break; | 
|  | case LZ4_MAX_BLOCK_SIZE_256K: | 
|  | *out = 256 * 1024; | 
|  | break; | 
|  | case LZ4_MAX_BLOCK_SIZE_1M: | 
|  | *out = 1024 * 1024; | 
|  | break; | 
|  | case LZ4_MAX_BLOCK_SIZE_4M: | 
|  | *out = 4 * 1024 * 1024; | 
|  | break; | 
|  | default: | 
|  | printf("Invalid block max size code"); | 
|  | return -1; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | int lz4_decompress(const struct lz4_frame_header *h, const uint8_t *src, | 
|  | size_t *srcn, uint8_t *dst, size_t *dstn, | 
|  | bool *has_more_data) | 
|  | { | 
|  | int ret = 0; | 
|  | size_t max_block_size = 0; | 
|  | if ((ret = get_lz4_max_block_size(h, &max_block_size))) { | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | const uint8_t *in_curr = src; | 
|  | const uint8_t *in_end = src + *srcn; | 
|  | uint8_t *out_curr = dst; | 
|  | uint8_t *out_end = dst + *dstn; | 
|  | *dstn = 0; | 
|  | *srcn = 0; | 
|  | *has_more_data = true; | 
|  | while (out_curr + max_block_size <= out_end) { | 
|  | size_t compressed_size = 0, decompressed_size = 0; | 
|  | if ((ret = decompress_block(in_curr, in_end - in_curr, out_curr, | 
|  | out_end - out_curr, | 
|  | &compressed_size, | 
|  | &decompressed_size))) { | 
|  | return ret; | 
|  | } | 
|  | in_curr += compressed_size + | 
|  | (h->has_block_checksum ? sizeof(u32) : 0); | 
|  | out_curr += decompressed_size; | 
|  | // If no more data can be decompressed, exit the loop | 
|  | if (!decompressed_size) { | 
|  | *has_more_data = false; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | *dstn = out_curr - dst; | 
|  | *srcn = in_curr - src; | 
|  | return 0; | 
|  | } |