diff options
| author | Lasse Collin <lasse.collin@tukaani.org> | 2011-01-12 20:01:23 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2011-01-13 11:03:25 -0500 |
| commit | 3ebe12439ba7fc62e1d6ecb569b7287771716ca1 (patch) | |
| tree | 65945a63ad1474489d80c8ca1fb1c1c8091fb7a2 | |
| parent | 24fa0402a9b6a537e87e38341e78b7da86486846 (diff) | |
decompressors: add boot-time XZ support
This implements the API defined in <linux/decompress/generic.h> which is
used for kernel, initramfs, and initrd decompression. This patch together
with the first patch is enough for XZ-compressed initramfs and initrd;
XZ-compressed kernel will need arch-specific changes.
The buffering requirements described in decompress_unxz.c are stricter
than with gzip, so the relevant changes should be done to the
arch-specific code when adding support for XZ-compressed kernel.
Similarly, the heap size in arch-specific pre-boot code may need to be
increased (30 KiB is enough).
The XZ decompressor needs memmove(), memeq() (memcmp() == 0), and
memzero() (memset(ptr, 0, size)), which aren't available in all
arch-specific pre-boot environments. I'm including simple versions in
decompress_unxz.c, but a cleaner solution would naturally be nicer.
Signed-off-by: Lasse Collin <lasse.collin@tukaani.org>
Cc: "H. Peter Anvin" <hpa@zytor.com>
Cc: Alain Knaff <alain@knaff.lu>
Cc: Albin Tonnerre <albin.tonnerre@free-electrons.com>
Cc: Phillip Lougher <phillip@lougher.demon.co.uk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/decompress/unxz.h | 19 | ||||
| -rw-r--r-- | init/Kconfig | 20 | ||||
| -rw-r--r-- | lib/Kconfig | 4 | ||||
| -rw-r--r-- | lib/Makefile | 1 | ||||
| -rw-r--r-- | lib/decompress.c | 5 | ||||
| -rw-r--r-- | lib/decompress_unxz.c | 397 | ||||
| -rw-r--r-- | scripts/gen_initramfs_list.sh | 2 | ||||
| -rw-r--r-- | usr/Kconfig | 18 | ||||
| -rw-r--r-- | usr/Makefile | 5 |
9 files changed, 469 insertions, 2 deletions
diff --git a/include/linux/decompress/unxz.h b/include/linux/decompress/unxz.h new file mode 100644 index 000000000000..41728fc6c8a1 --- /dev/null +++ b/include/linux/decompress/unxz.h | |||
| @@ -0,0 +1,19 @@ | |||
| 1 | /* | ||
| 2 | * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd | ||
| 3 | * | ||
| 4 | * Author: Lasse Collin <lasse.collin@tukaani.org> | ||
| 5 | * | ||
| 6 | * This file has been put into the public domain. | ||
| 7 | * You can do whatever you want with this file. | ||
| 8 | */ | ||
| 9 | |||
| 10 | #ifndef DECOMPRESS_UNXZ_H | ||
| 11 | #define DECOMPRESS_UNXZ_H | ||
| 12 | |||
| 13 | int unxz(unsigned char *in, int in_size, | ||
| 14 | int (*fill)(void *dest, unsigned int size), | ||
| 15 | int (*flush)(void *src, unsigned int size), | ||
| 16 | unsigned char *out, int *in_used, | ||
| 17 | void (*error)(char *x)); | ||
| 18 | |||
| 19 | #endif | ||
diff --git a/init/Kconfig b/init/Kconfig index 8dfd094e6875..ea176e8edbdd 100644 --- a/init/Kconfig +++ b/init/Kconfig | |||
| @@ -130,13 +130,16 @@ config HAVE_KERNEL_BZIP2 | |||
| 130 | config HAVE_KERNEL_LZMA | 130 | config HAVE_KERNEL_LZMA |
| 131 | bool | 131 | bool |
| 132 | 132 | ||
| 133 | config HAVE_KERNEL_XZ | ||
| 134 | bool | ||
| 135 | |||
| 133 | config HAVE_KERNEL_LZO | 136 | config HAVE_KERNEL_LZO |
| 134 | bool | 137 | bool |
| 135 | 138 | ||
| 136 | choice | 139 | choice |
| 137 | prompt "Kernel compression mode" | 140 | prompt "Kernel compression mode" |
| 138 | default KERNEL_GZIP | 141 | default KERNEL_GZIP |
| 139 | depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_LZO | 142 | depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO |
| 140 | help | 143 | help |
| 141 | The linux kernel is a kind of self-extracting executable. | 144 | The linux kernel is a kind of self-extracting executable. |
| 142 | Several compression algorithms are available, which differ | 145 | Several compression algorithms are available, which differ |
| @@ -181,6 +184,21 @@ config KERNEL_LZMA | |||
| 181 | two. Compression is slowest. The kernel size is about 33% | 184 | two. Compression is slowest. The kernel size is about 33% |
| 182 | smaller with LZMA in comparison to gzip. | 185 | smaller with LZMA in comparison to gzip. |
| 183 | 186 | ||
| 187 | config KERNEL_XZ | ||
| 188 | bool "XZ" | ||
| 189 | depends on HAVE_KERNEL_XZ | ||
| 190 | help | ||
| 191 | XZ uses the LZMA2 algorithm and instruction set specific | ||
| 192 | BCJ filters which can improve compression ratio of executable | ||
| 193 | code. The size of the kernel is about 30% smaller with XZ in | ||
| 194 | comparison to gzip. On architectures for which there is a BCJ | ||
| 195 | filter (i386, x86_64, ARM, IA-64, PowerPC, and SPARC), XZ | ||
| 196 | will create a few percent smaller kernel than plain LZMA. | ||
| 197 | |||
| 198 | The speed is about the same as with LZMA: The decompression | ||
| 199 | speed of XZ is better than that of bzip2 but worse than gzip | ||
| 200 | and LZO. Compression is slow. | ||
| 201 | |||
| 184 | config KERNEL_LZO | 202 | config KERNEL_LZO |
| 185 | bool "LZO" | 203 | bool "LZO" |
| 186 | depends on HAVE_KERNEL_LZO | 204 | depends on HAVE_KERNEL_LZO |
diff --git a/lib/Kconfig b/lib/Kconfig index 2b8f8540d670..0ee67e08ad3e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig | |||
| @@ -122,6 +122,10 @@ config DECOMPRESS_BZIP2 | |||
| 122 | config DECOMPRESS_LZMA | 122 | config DECOMPRESS_LZMA |
| 123 | tristate | 123 | tristate |
| 124 | 124 | ||
| 125 | config DECOMPRESS_XZ | ||
| 126 | select XZ_DEC | ||
| 127 | tristate | ||
| 128 | |||
| 125 | config DECOMPRESS_LZO | 129 | config DECOMPRESS_LZO |
| 126 | select LZO_DECOMPRESS | 130 | select LZO_DECOMPRESS |
| 127 | tristate | 131 | tristate |
diff --git a/lib/Makefile b/lib/Makefile index 4df2d0297721..cbb774f7d41d 100644 --- a/lib/Makefile +++ b/lib/Makefile | |||
| @@ -75,6 +75,7 @@ obj-$(CONFIG_RAID6_PQ) += raid6/ | |||
| 75 | lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o | 75 | lib-$(CONFIG_DECOMPRESS_GZIP) += decompress_inflate.o |
| 76 | lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o | 76 | lib-$(CONFIG_DECOMPRESS_BZIP2) += decompress_bunzip2.o |
| 77 | lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o | 77 | lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o |
| 78 | lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o | ||
| 78 | lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o | 79 | lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o |
| 79 | 80 | ||
| 80 | obj-$(CONFIG_TEXTSEARCH) += textsearch.o | 81 | obj-$(CONFIG_TEXTSEARCH) += textsearch.o |
diff --git a/lib/decompress.c b/lib/decompress.c index a7606815541f..3d766b7f60ab 100644 --- a/lib/decompress.c +++ b/lib/decompress.c | |||
| @@ -8,6 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include <linux/decompress/bunzip2.h> | 9 | #include <linux/decompress/bunzip2.h> |
| 10 | #include <linux/decompress/unlzma.h> | 10 | #include <linux/decompress/unlzma.h> |
| 11 | #include <linux/decompress/unxz.h> | ||
| 11 | #include <linux/decompress/inflate.h> | 12 | #include <linux/decompress/inflate.h> |
| 12 | #include <linux/decompress/unlzo.h> | 13 | #include <linux/decompress/unlzo.h> |
| 13 | 14 | ||
| @@ -23,6 +24,9 @@ | |||
| 23 | #ifndef CONFIG_DECOMPRESS_LZMA | 24 | #ifndef CONFIG_DECOMPRESS_LZMA |
| 24 | # define unlzma NULL | 25 | # define unlzma NULL |
| 25 | #endif | 26 | #endif |
| 27 | #ifndef CONFIG_DECOMPRESS_XZ | ||
| 28 | # define unxz NULL | ||
| 29 | #endif | ||
| 26 | #ifndef CONFIG_DECOMPRESS_LZO | 30 | #ifndef CONFIG_DECOMPRESS_LZO |
| 27 | # define unlzo NULL | 31 | # define unlzo NULL |
| 28 | #endif | 32 | #endif |
| @@ -36,6 +40,7 @@ static const struct compress_format { | |||
| 36 | { {037, 0236}, "gzip", gunzip }, | 40 | { {037, 0236}, "gzip", gunzip }, |
| 37 | { {0x42, 0x5a}, "bzip2", bunzip2 }, | 41 | { {0x42, 0x5a}, "bzip2", bunzip2 }, |
| 38 | { {0x5d, 0x00}, "lzma", unlzma }, | 42 | { {0x5d, 0x00}, "lzma", unlzma }, |
| 43 | { {0xfd, 0x37}, "xz", unxz }, | ||
| 39 | { {0x89, 0x4c}, "lzo", unlzo }, | 44 | { {0x89, 0x4c}, "lzo", unlzo }, |
| 40 | { {0, 0}, NULL, NULL } | 45 | { {0, 0}, NULL, NULL } |
| 41 | }; | 46 | }; |
diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c new file mode 100644 index 000000000000..cecd23df2b9a --- /dev/null +++ b/lib/decompress_unxz.c | |||
| @@ -0,0 +1,397 @@ | |||
| 1 | /* | ||
| 2 | * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd | ||
| 3 | * | ||
| 4 | * Author: Lasse Collin <lasse.collin@tukaani.org> | ||
| 5 | * | ||
| 6 | * This file has been put into the public domain. | ||
| 7 | * You can do whatever you want with this file. | ||
| 8 | */ | ||
| 9 | |||
| 10 | /* | ||
| 11 | * Important notes about in-place decompression | ||
| 12 | * | ||
| 13 | * At least on x86, the kernel is decompressed in place: the compressed data | ||
| 14 | * is placed to the end of the output buffer, and the decompressor overwrites | ||
| 15 | * most of the compressed data. There must be enough safety margin to | ||
| 16 | * guarantee that the write position is always behind the read position. | ||
| 17 | * | ||
| 18 | * The safety margin for XZ with LZMA2 or BCJ+LZMA2 is calculated below. | ||
| 19 | * Note that the margin with XZ is bigger than with Deflate (gzip)! | ||
| 20 | * | ||
| 21 | * The worst case for in-place decompression is that the beginning of | ||
| 22 | * the file is compressed extremely well, and the rest of the file is | ||
| 23 | * uncompressible. Thus, we must look for worst-case expansion when the | ||
| 24 | * compressor is encoding uncompressible data. | ||
| 25 | * | ||
| 26 | * The structure of the .xz file in case of a compresed kernel is as follows. | ||
| 27 | * Sizes (as bytes) of the fields are in parenthesis. | ||
| 28 | * | ||
| 29 | * Stream Header (12) | ||
| 30 | * Block Header: | ||
| 31 | * Block Header (8-12) | ||
| 32 | * Compressed Data (N) | ||
| 33 | * Block Padding (0-3) | ||
| 34 | * CRC32 (4) | ||
| 35 | * Index (8-20) | ||
| 36 | * Stream Footer (12) | ||
| 37 | * | ||
| 38 | * Normally there is exactly one Block, but let's assume that there are | ||
| 39 | * 2-4 Blocks just in case. Because Stream Header and also Block Header | ||
| 40 | * of the first Block don't make the decompressor produce any uncompressed | ||
| 41 | * data, we can ignore them from our calculations. Block Headers of possible | ||
| 42 | * additional Blocks have to be taken into account still. With these | ||
| 43 | * assumptions, it is safe to assume that the total header overhead is | ||
| 44 | * less than 128 bytes. | ||
| 45 | * | ||
| 46 | * Compressed Data contains LZMA2 or BCJ+LZMA2 encoded data. Since BCJ | ||
| 47 | * doesn't change the size of the data, it is enough to calculate the | ||
| 48 | * safety margin for LZMA2. | ||
| 49 | * | ||
| 50 | * LZMA2 stores the data in chunks. Each chunk has a header whose size is | ||
| 51 | * a maximum of 6 bytes, but to get round 2^n numbers, let's assume that | ||
| 52 | * the maximum chunk header size is 8 bytes. After the chunk header, there | ||
| 53 | * may be up to 64 KiB of actual payload in the chunk. Often the payload is | ||
| 54 | * quite a bit smaller though; to be safe, let's assume that an average | ||
| 55 | * chunk has only 32 KiB of payload. | ||
| 56 | * | ||
| 57 | * The maximum uncompressed size of the payload is 2 MiB. The minimum | ||
| 58 | * uncompressed size of the payload is in practice never less than the | ||
| 59 | * payload size itself. The LZMA2 format would allow uncompressed size | ||
| 60 | * to be less than the payload size, but no sane compressor creates such | ||
| 61 | * files. LZMA2 supports storing uncompressible data in uncompressed form, | ||
| 62 | * so there's never a need to create payloads whose uncompressed size is | ||
| 63 | * smaller than the compressed size. | ||
| 64 | * | ||
| 65 | * The assumption, that the uncompressed size of the payload is never | ||
| 66 | * smaller than the payload itself, is valid only when talking about | ||
