aboutsummaryrefslogtreecommitdiff
path: root/linux/lib/xz/xz_boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'linux/lib/xz/xz_boot.c')
-rw-r--r--linux/lib/xz/xz_boot.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/linux/lib/xz/xz_boot.c b/linux/lib/xz/xz_boot.c
new file mode 100644
index 0000000..9a1adfb
--- /dev/null
+++ b/linux/lib/xz/xz_boot.c
@@ -0,0 +1,180 @@
+/*
+ * XZ decoder as a single file for uncompressing the kernel and initramfs
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * Before #including this file, void error(char *) or void error(const char *)
+ * must have been declared. It is called from xz_dec_buf() if an error
+ * occurs; if error() returns, xz_dec_buf() will return false. On success,
+ * xz_dec_buf() returns true, but if error() would never return, there's no
+ * need to check the return value of xz_dec_buf().
+ *
+ * In the early phase of boot where kmalloc, memmove, and friends are not
+ * available, you need to #define XZ_MEM_FUNCS before #including this file.
+ * Before calling xz_dec_buf, set malloc_buf to the beginning of a workspace
+ * buffer, and malloc_avail to the size of the workspace buffer. malloc_buf
+ * must be aligned to a multiple of 8 bytes. About 30 KiB of space is
+ * currently needed, but to be future-proof, having 40-50 KiB of space would
+ * be good.
+ *
+ * Example:
+ *
+ * static void error(const char *message);
+ * #define XZ_MEM_FUNCS
+ * #include "../../../../lib/xz/xz_boot.c"
+ * ...
+ * struct xz_buf b;
+ * b.in = (const uint8_t *)in_buf;
+ * b.in_pos = 0;
+ * b.in_size = in_buf_size;
+ * b.out = (uint8_t *)out_buf;
+ * b.out_pos = 0;
+ * b.out_size = out_buf_size_max;
+ * malloc_buf = (uint8_t *)free_mem_begin;
+ * malloc_avail = free_mem_avail;
+ * if (!xz_dec_buf(&b))
+ * goto error_handler;
+ *
+ * If xz_dec_buf() succeeded (it returned true), the amount if input consumed
+ * is in b.in_pos and the amount of output used is in b.out_pos.
+ */
+
+#if !defined(XZ_FUNC) && defined(INIT)
+# define XZ_FUNC INIT
+#endif
+#define XZ_INTERNAL_CRC32
+#include "xz_private.h"
+
+#ifdef XZ_MEM_FUNCS
+/*
+ * Very simple malloc, which just picks big enough chunk from a preallocated
+ * buffer. There's no free(), because the decoder is (at least for now) used
+ * only once per compilation unit. Thus, free() would be just waste of space.
+ */
+static uint8_t *malloc_buf;
+static size_t malloc_avail;
+
+static void * XZ_FUNC malloc(size_t size)
+{
+ void *ptr = malloc_buf;
+ size = (size + 7) & ~(size_t)7;
+ if (size > malloc_avail)
+ return NULL;
+
+ malloc_buf += size;
+ malloc_avail -= size;
+ return ptr;
+}
+
+/*
+ * memeq and memzero are not used much and any remotely sane implementation
+ * is fast enough. memcpy/memmove speed matters in multi-call mode, but
+ * xz_dec_buf() uses only the single-call mode, in which only memcpy speed
+ * can matter and only if there is a lot of uncompressible data (LZMA2 stores
+ * uncompressible chunks in uncompressed form). Thus, the functions below
+ * should just be kept small; it's probably not worth optimizing for speed.
+ */
+static bool XZ_FUNC memeq(const void *a, const void *b, size_t size)
+{
+ const uint8_t *x = a;
+ const uint8_t *y = b;
+ size_t i;
+
+ for (i = 0; i < size; ++i)
+ if (x[i] != y[i])
+ return false;
+
+ return true;
+}
+
+static void XZ_FUNC memzero(void *buf, size_t size)
+{
+ uint8_t *b = buf;
+ uint8_t *e = b + size;
+ while (b != e)
+ *b++ = '\0';
+}
+
+static void * XZ_FUNC memmove(void *dest, const void *src, size_t size)
+{
+ uint8_t *d = dest;
+ const uint8_t *s = src;
+ size_t i;
+
+ if (d < s) {
+ for (i = 0; i < size; ++i)
+ d[i] = s[i];
+ } else if (d > s) {
+ i = size;
+ while (i-- > 0)
+ d[i] = s[i];
+ }
+
+ return dest;
+}
+
+/* Since we need memmove anyway, use it as memcpy too. */
+#define memcpy memmove
+#endif
+
+#include "xz_crc32.c"
+#include "xz_dec_stream.c"
+#include "xz_dec_lzma2.c"
+
+/**
+ * xz_dec_buf() - Single-call XZ decoder
+ * @b: Input and output buffers
+ *
+ * On success, true is returned. On error, error() is called and false is
+ * returned in case error() returns.
+ */
+static bool XZ_FUNC xz_dec_buf(struct xz_buf *b)
+{
+ struct xz_dec *s;
+ enum xz_ret ret;
+
+ xz_crc32_init();
+
+ s = xz_dec_init(0);
+ if (s == NULL) {
+ error("XZ decoder ran out of memory");
+ return false;
+ }
+
+ ret = xz_dec_run(s, b);
+ xz_dec_end(s);
+
+ switch (ret) {
+ case XZ_STREAM_END:
+ return true;
+
+ case XZ_FORMAT_ERROR:
+ error("Input is not in the XZ format (wrong magic bytes)");
+ break;
+
+ case XZ_OPTIONS_ERROR:
+ error("Input was encoded with settings that are not "
+ "supported by this XZ decoder");
+ break;
+
+ case XZ_DATA_ERROR:
+ error("XZ-compressed data is corrupt");
+ break;
+
+ case XZ_BUF_ERROR:
+ error("Output buffer is too small or the "
+ "XZ-compressed data is corrupt");
+ break;
+
+ default:
+ error("Bug in the XZ decoder");
+ break;
+ }
+
+ return false;
+}