summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/Kconfig51
-rw-r--r--lib/Kconfig.hardening119
-rw-r--r--lib/Makefile29
-rw-r--r--lib/base64.c60
-rw-r--r--lib/bcd.c2
-rw-r--r--lib/bitmap.c113
-rw-r--r--lib/blobgen.c4
-rw-r--r--lib/bootstrap/Kconfig2
-rw-r--r--lib/bootstrap/Makefile2
-rw-r--r--lib/bootstrap/devfs.c21
-rw-r--r--lib/bootstrap/disk.c5
-rw-r--r--lib/bsearch.c34
-rw-r--r--lib/cmdlinepart.c9
-rw-r--r--lib/decompress_bunzip2.c16
-rw-r--r--lib/decompress_inflate.c10
-rw-r--r--lib/decompress_unlz4.c17
-rw-r--r--lib/decompress_unlzo.c8
-rw-r--r--lib/decompress_unxz.c16
-rw-r--r--lib/decompress_unzstd.c320
-rw-r--r--lib/div64.c54
-rw-r--r--lib/fdt.c1
-rw-r--r--lib/fdt_empty_tree.c2
-rw-r--r--lib/fdt_ro.c2
-rw-r--r--lib/fdt_rw.c2
-rw-r--r--lib/fdt_strerror.c1
-rw-r--r--lib/fdt_sw.c2
-rw-r--r--lib/fdt_wip.c2
-rw-r--r--lib/fnmatch.c45
-rw-r--r--lib/fonts/Kconfig2
-rw-r--r--lib/fonts/Makefile2
-rw-r--r--lib/fonts/font_6x8.c2
-rw-r--r--lib/fonts/font_7x14.c2
-rw-r--r--lib/fonts/font_8x16.c2
-rw-r--r--lib/fonts/font_8x8.c2
-rw-r--r--lib/fonts/font_custom_16x.c2
-rw-r--r--lib/fonts/font_mini_4x6.c2
-rw-r--r--lib/fonts/fonts.c8
-rw-r--r--lib/gcd.c2
-rw-r--r--lib/genalloc.c120
-rw-r--r--lib/getopt.c17
-rw-r--r--lib/glob.c107
-rw-r--r--lib/gui/2d-primitives.c2
-rw-r--r--lib/gui/Kconfig7
-rw-r--r--lib/gui/Makefile3
-rw-r--r--lib/gui/bmp.c2
-rw-r--r--lib/gui/graphic_utils.c18
-rw-r--r--lib/gui/picopng.h2
-rw-r--r--lib/gui/png.c2
-rw-r--r--lib/gui/png_lode.c2
-rw-r--r--lib/gui/png_pico.c2
-rw-r--r--lib/gui/qoi.c97
-rw-r--r--lib/gui/qoi.h673
-rw-r--r--lib/hexdump.c226
-rw-r--r--lib/idr.c100
-rw-r--r--lib/image-sparse.c11
-rw-r--r--lib/jsmn.c505
-rw-r--r--lib/kasan/Kconfig16
-rw-r--r--lib/kasan/Makefile15
-rw-r--r--lib/kasan/common.c108
-rw-r--r--lib/kasan/generic.c318
-rw-r--r--lib/kasan/generic_report.c150
-rw-r--r--lib/kasan/kasan.h164
-rw-r--r--lib/kasan/report.c199
-rw-r--r--lib/kasan/test_kasan.c492
-rw-r--r--lib/libbb.c58
-rw-r--r--lib/libfile.c220
-rw-r--r--lib/libscan.c2
-rw-r--r--lib/list_debug.c68
-rw-r--r--lib/list_sort.c156
-rw-r--r--lib/logo/.gitignore17
-rw-r--r--lib/logo/Kconfig34
-rw-r--r--lib/logo/Makefile61
-rw-r--r--lib/logo/logo.c10
-rw-r--r--lib/lz4/Makefile2
-rw-r--r--lib/lzo/Kconfig6
-rw-r--r--lib/lzo/Makefile3
-rw-r--r--lib/lzo/lzo1x_compress.c279
-rw-r--r--lib/make_directory.c4
-rw-r--r--lib/math/Makefile5
-rw-r--r--lib/math/div64.c235
-rw-r--r--lib/math/rational.c105
-rw-r--r--lib/misc.c7
-rw-r--r--lib/nls_base.c2
-rw-r--r--lib/notifier.c2
-rw-r--r--lib/parameter.c254
-rw-r--r--lib/parseopt.c33
-rw-r--r--lib/random.c31
-rw-r--r--lib/ratp.c30
-rw-r--r--lib/readkey.c2
-rw-r--r--lib/readline.c19
-rw-r--r--lib/readline_simple.c4
-rw-r--r--lib/reed_solomon/Makefile2
-rw-r--r--lib/reed_solomon/reed_solomon.c2
-rw-r--r--lib/refcount.c34
-rw-r--r--lib/show_progress.c47
-rw-r--r--lib/stackprot.c44
-rw-r--r--lib/string.c369
-rw-r--r--lib/stringlist.c55
-rw-r--r--lib/strtox.c2
-rw-r--r--lib/strverscmp.c165
-rw-r--r--lib/ubsan.c108
-rw-r--r--lib/uncompress.c75
-rw-r--r--lib/unlink-recursive.c2
-rw-r--r--lib/uuid.c109
-rw-r--r--lib/vsprintf.c323
-rw-r--r--lib/wchar.c31
-rw-r--r--lib/xz/Makefile2
-rw-r--r--lib/xz/xz_dec_bcj.c54
-rw-r--r--lib/xz/xz_private.h5
-rw-r--r--lib/zlib_inflate/Makefile2
-rw-r--r--lib/zstd/Makefile2
111 files changed, 6257 insertions, 1158 deletions
diff --git a/lib/Kconfig b/lib/Kconfig
index b4a8079700..71715ef6e8 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
menu "Library routines"
config PARAMETER
bool
@@ -6,6 +8,11 @@ config UNCOMPRESS
bool
select FILETYPE
+config JSMN
+ bool "JSMN JSON Parser" if COMPILE_TEST
+ help
+ A minimalistic JSON parser.
+
config XXHASH
bool
@@ -35,6 +42,7 @@ config XZ_DECOMPRESS
select XZ_DEC_ARM
select XZ_DEC_ARMTHUMB
select XZ_DEC_SPARC
+ select XZ_DEC_ARM64
config XZ_DEC_X86
bool
@@ -54,12 +62,18 @@ config XZ_DEC_ARMTHUMB
config XZ_DEC_SPARC
bool
+config XZ_DEC_ARM64
+ bool
+
config REED_SOLOMON
bool
config BASE64
bool "include base64 encode/decode support"
+config CONSTRUCTORS
+ bool
+
config GENERIC_FIND_NEXT_BIT
def_bool n
@@ -108,7 +122,7 @@ config LIBFDT
config RATP
select CRC_ITU_T
- select COMPILE_MEMORY
+ select DEV_MEM
select COMMAND_SUPPORT
select POLLER
depends on CONSOLE_FULL
@@ -151,8 +165,30 @@ source "lib/logo/Kconfig"
source "lib/bootstrap/Kconfig"
+config PROGRESS_NOTIFIER
+ bool "Progress Notifier" if COMPILE_TEST
+ help
+ This is selected by boards that register a notifier to visualize
+ progress, like blinking a LED during an update.
+
+config VERSION_CMP
+ bool
+ help
+ This is selected by code that needs to compare versions
+ in a manner compatible with
+ https://uapi-group.org/specifications/specs/version_format_specification
+
config PRINTF_UUID
bool
+ default y if PRINTF_FULL
+
+config PRINTF_WCHAR
+ bool
+ default y if PRINTF_FULL
+
+config PRINTF_HEXSTR
+ bool
+ default y if PRINTF_FULL
config GENERIC_LIB_ASHLDI3
bool
@@ -179,7 +215,20 @@ config ARCH_HAS_STACK_DUMP
config ARCH_HAS_DATA_ABORT_MASK
bool
+config ARCH_HAS_ZERO_PAGE
+ bool
+
config HAVE_EFFICIENT_UNALIGNED_ACCESS
bool
+config GENERIC_ALLOCATOR
+ bool
+ help
+ Support is curently limited to allocaing a complete mmio-sram at once.
+
+config IDR
+ bool
+
endmenu
+
+source "lib/Kconfig.hardening"
diff --git a/lib/Kconfig.hardening b/lib/Kconfig.hardening
new file mode 100644
index 0000000000..28be42a274
--- /dev/null
+++ b/lib/Kconfig.hardening
@@ -0,0 +1,119 @@
+menu "Hardening options"
+
+config BUG_ON_DATA_CORRUPTION
+ bool "Trigger a BUG when data corruption is detected"
+ select DEBUG_LIST
+ help
+ Select this option if barebox should BUG when it encounters
+ data corruption in its memory structures when they get checked
+ for validity.
+
+ If unsure, say N.
+
+config STACK_GUARD_PAGE
+ bool "Place guard page to catch stack overflows"
+ depends on ARM && MMU
+ help
+ When enabled, barebox places a faulting guard page to catch total
+ stack usage exceeding CONFIG_STACK_SIZE. On overflows, that hit
+ the reserved 4KiB, barebox will panic and report a stack overflow.
+ The report may not always succeed if the stack overflow impacts
+ operation of the exception handler.
+
+config STACKPROTECTOR
+ bool
+
+choice
+ prompt "Stack Protector buffer overflow detection"
+
+config STACKPROTECTOR_NONE
+ bool "None"
+
+config STACKPROTECTOR_STRONG
+ bool "Strong"
+ depends on $(cc-option,-fstack-protector-strong)
+ select STACKPROTECTOR
+ help
+ This option turns on the "stack-protector" GCC feature. This
+ feature puts, at the beginning of functions, a canary value on
+ the stack just before the return address, and validates
+ the value just before actually returning. Stack based buffer
+ overflows (that need to overwrite this return address) now also
+ overwrite the canary, which gets detected and the attack is then
+ neutralized via a barebox panic.
+
+ Functions will have the stack-protector canary logic added in any
+ of the following conditions:
+
+ - local variable's address used as part of the right hand side of an
+ assignment or function argument
+ - local variable is an array (or union containing an array),
+ regardless of array type or length
+ - uses register local variables
+
+ The canary will be a fixed value at first, but will be replaced by
+ one generated from a hardware random number generator if available
+ later on.
+
+config STACKPROTECTOR_ALL
+ bool "All"
+ depends on $(cc-option,-fstack-protector-all)
+ depends on COMPILE_TEST
+ select STACKPROTECTOR
+ help
+ This pushes and verifies stack protector canaries on all functions,
+ even those that don't need it. As this implies injection of a
+ global variable dependency on every function, this option is useful
+ for crashing functions called prior to prerelocation, which lack a
+ __prereloc attribute. This is likely the only upside compared to
+ the strong variant, so it's not selectable by default.
+
+endchoice
+
+choice
+ prompt "Stack Protector buffer overflow detection for PBL" if PBL_IMAGE
+
+config PBL_STACKPROTECTOR_NONE
+ bool "None"
+
+config PBL_STACKPROTECTOR_STRONG
+ bool "Strong"
+ depends on $(cc-option,-fstack-protector-strong)
+ depends on PBL_IMAGE
+ select STACKPROTECTOR
+ help
+ For PBL, This option turns on the "stack-protector" GCC feature. This
+ feature puts, at the beginning of functions, a canary value on
+ the stack just before the return address, and validates
+ the value just before actually returning. Stack based buffer
+ overflows (that need to overwrite this return address) now also
+ overwrite the canary, which gets detected and the attack is then
+ neutralized via a barebox panic.
+
+ Functions will have the stack-protector canary logic added in any
+ of the following conditions:
+
+ - local variable's address used as part of the right hand side of an
+ assignment or function argument
+ - local variable is an array (or union containing an array),
+ regardless of array type or length
+ - uses register local variables
+
+ The canary is always a fixed value.
+
+config PBL_STACKPROTECTOR_ALL
+ bool "PBL"
+ depends on $(cc-option,-fstack-protector-strong)
+ depends on PBL_IMAGE && COMPILE_TEST
+ select STACKPROTECTOR
+ help
+ This pushes and verifies stack protector canaries on all functions,
+ even those that don't need it. As this implies injection of a
+ global variable dependency on every function, this option is useful
+ for crashing functions called prior to prerelocation, which lack a
+ __prereloc attribute. This is likely the only upside compared to
+ the strong variant.
+
+endchoice
+
+endmenu
diff --git a/lib/Makefile b/lib/Makefile
index 56040a0d5f..54866f59cc 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -1,15 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-y += bcd.o
obj-$(CONFIG_BOOTSTRAP) += bootstrap/
-obj-y += ctype.o
+obj-pbl-y += ctype.o
obj-y += rbtree.o
obj-y += display_options.o
obj-y += string.o
+obj-$(CONFIG_VERSION_CMP) += strverscmp.o
obj-y += strtox.o
obj-y += kstrtox.o
obj-y += vsprintf.o
+obj-$(CONFIG_KASAN) += kasan/
+obj-pbl-$(CONFIG_STACKPROTECTOR) += stackprot.o
pbl-$(CONFIG_PBL_CONSOLE) += vsprintf.o
-obj-y += div64.o
-pbl-y += div64.o
obj-y += misc.o
obj-$(CONFIG_PARAMETER) += parameter.o
obj-y += xfuncs.o
@@ -18,12 +21,16 @@ obj-y += readkey.o
obj-y += kfifo.o
obj-y += libbb.o
obj-y += libgen.o
+obj-$(CONFIG_JSMN) += jsmn.o
obj-$(CONFIG_BLOBGEN) += blobgen.o
obj-y += stringlist.o
obj-y += cmdlinepart.o
obj-y += recursive_action.o
obj-y += make_directory.o
obj-y += math.o
+obj-$(CONFIG_IDR) += idr.o
+obj-y += math/
+obj-y += uuid.o
obj-$(CONFIG_XXHASH) += xxhash.o
obj-$(CONFIG_BZLIB) += decompress_bunzip2.o
obj-$(CONFIG_ZLIB) += decompress_inflate.o zlib_inflate/
@@ -32,7 +39,7 @@ obj-$(CONFIG_CMDLINE_EDITING) += readline.o
obj-$(CONFIG_SIMPLE_READLINE) += readline_simple.o
obj-$(CONFIG_FNMATCH) += fnmatch.o
obj-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
-obj-y += glob.o
+obj-$(CONFIG_GLOB) += glob.o
obj-y += notifier.o
obj-y += random.o
obj-$(CONFIG_IMAGE_SPARSE) += image-sparse.o
@@ -42,6 +49,7 @@ obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd/
obj-y += show_progress.o
obj-$(CONFIG_LZO_DECOMPRESS) += decompress_unlzo.o
obj-$(CONFIG_LZ4_DECOMPRESS) += decompress_unlz4.o
+obj-$(CONFIG_ZSTD_DECOMPRESS) += decompress_unzstd.o
obj-$(CONFIG_PROCESS_ESCAPE_SEQUENCE) += process_escape_sequence.o
obj-$(CONFIG_UNCOMPRESS) += uncompress.o
obj-$(CONFIG_BCH) += bch.o
@@ -58,12 +66,15 @@ obj-y += wchar.o
obj-y += libfile.o
obj-y += bitmap.o
obj-y += gcd.o
-obj-y += hexdump.o
+obj-y += bsearch.o
+obj-pbl-y += hexdump.o
obj-$(CONFIG_FONTS) += fonts/
obj-$(CONFIG_BAREBOX_LOGO) += logo/
obj-y += reed_solomon/
obj-$(CONFIG_RATP) += ratp.o
+obj-$(CONFIG_DEBUG_LIST) += list_debug.o
obj-y += list_sort.o
+obj-y += refcount.o
obj-y += int_sqrt.o
obj-y += parseopt.o
obj-y += clz_ctz.o
@@ -72,6 +83,7 @@ obj-$(CONFIG_CRC8) += crc8.o
obj-$(CONFIG_NLS) += nls_base.o
obj-$(CONFIG_FSL_QE_FIRMWARE) += fsl-qe-firmware.o
obj-$(CONFIG_UBSAN) += ubsan.o
+obj-$(CONFIG_GENERIC_ALLOCATOR) += genalloc.o
# GCC library routines
obj-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o
@@ -82,12 +94,13 @@ obj-$(CONFIG_GENERIC_LIB_MULDI3) += muldi3.o
pbl-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o
UBSAN_SANITIZE_ubsan.o := n
+KASAN_SANITIZE_ubsan.o := n
+CFLAGS_ubsan.o := -fno-stack-protector
libfdt_files = fdt.o fdt_ro.o fdt_wip.o fdt_rw.o fdt_sw.o fdt_strerror.o \
fdt_empty_tree.o
$(foreach file, $(libfdt_files), \
- $(eval CFLAGS_$(file) = -I $(srctree)/scripts/dtc/libfdt))
-$(foreach file, $(libfdt_files), \
- $(eval CFLAGS_pbl-$(file) = -I $(srctree)/scripts/dtc/libfdt))
+ $(eval CFLAGS_$(file) = -I $(srctree)/scripts/dtc/libfdt) \
+ $(eval CFLAGS_$(file:%.o=%.pbl.o) = -I $(srctree)/scripts/dtc/libfdt))
obj-pbl-$(CONFIG_LIBFDT) += $(libfdt_files)
diff --git a/lib/base64.c b/lib/base64.c
index ac165ab168..d5ab217528 100644
--- a/lib/base64.c
+++ b/lib/base64.c
@@ -25,6 +25,25 @@ static const char uuenc_tbl_base64[65 + 1] = {
'\0' /* needed for uudecode.c only */
};
+static char base64_trchr(char ch, bool url)
+{
+ if (!url)
+ return ch;
+
+ switch (ch) {
+ case '+':
+ return '-';
+ case '/':
+ return '_';
+ case '-':
+ return '+';
+ case '_':
+ return '/';
+ default:
+ return ch;
+ }
+}
+
/*
* Encode bytes at S of length LENGTH to uuencode or base64 format and place it
* to STORE. STORE will be 0-terminated, and must point to a writable
@@ -68,13 +87,14 @@ EXPORT_SYMBOL(uuencode);
* Decode base64 encoded string. Stops on '\0'.
*
*/
-int decode_base64(char *p_dst, int dst_len, const char *src)
+static int __decode_base64(char *p_dst, int dst_len, const char *src, bool url)
{
const char *src_tail;
char *dst = p_dst;
int length = 0;
+ bool end_reached = false;
- while (dst_len > 0) {
+ while (dst_len > 0 && !end_reached) {
unsigned char six_bit[4];
int count = 0;
@@ -101,13 +121,23 @@ int decode_base64(char *p_dst, int dst_len, const char *src)
* because we did fully decode
* the string (to "ABC").
*/
- if (count == 0)
+ if (count == 0) {
src_tail = src;
+ } else if (url) {
+ end_reached = true;
+ goto out;
+ }
+
goto ret;
}
src++;
- table_ptr = strchr(uuenc_tbl_base64, ch);
- } while (!table_ptr);
+ table_ptr = strchr(uuenc_tbl_base64, base64_trchr(ch, url));
+ } while (!table_ptr && !url);
+
+ if (!table_ptr) {
+ end_reached = true;
+ goto out;
+ }
/* Convert encoded character to decimal */
ch = table_ptr - uuenc_tbl_base64;
@@ -119,6 +149,7 @@ int decode_base64(char *p_dst, int dst_len, const char *src)
six_bit[count] = ch;
count++;
}
+out:
/*
* Transform 6-bit values to 8-bit ones.
@@ -151,4 +182,23 @@ ret:
return length;
}
+
+/*
+ * Decode base64 encoded string. Stops on '\0'.
+ *
+ */
+int decode_base64(char *p_dst, int dst_len, const char *src)
+{
+ return __decode_base64(p_dst, dst_len, src, false);
+}
EXPORT_SYMBOL(decode_base64);
+
+/*
+ * Decode base64url encoded string. Stops on '\0'.
+ *
+ */
+int decode_base64url(char *p_dst, int dst_len, const char *src)
+{
+ return __decode_base64(p_dst, dst_len, src, true);
+}
+EXPORT_SYMBOL(decode_base64url);
diff --git a/lib/bcd.c b/lib/bcd.c
index b072d50a90..4bed6eddfe 100644
--- a/lib/bcd.c
+++ b/lib/bcd.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <linux/bcd.h>
#include <module.h>
diff --git a/lib/bitmap.c b/lib/bitmap.c
index 5be6651941..7614c4d213 100644
--- a/lib/bitmap.c
+++ b/lib/bitmap.c
@@ -837,3 +837,116 @@ void bitmap_copy_le(void *dst, const unsigned long *src, int nbits)
}
}
EXPORT_SYMBOL(bitmap_copy_le);
+
+unsigned long *bitmap_zalloc(unsigned int nbits)
+{
+ return calloc(BITS_TO_LONGS(nbits), sizeof(unsigned long));
+}
+
+unsigned long *bitmap_xzalloc(unsigned int nbits)
+{
+ return xzalloc(BITS_TO_LONGS(nbits) * sizeof(unsigned long));
+}
+
+#if BITS_PER_LONG == 64
+/**
+ * bitmap_from_arr32 - copy the contents of u32 array of bits to bitmap
+ * @bitmap: array of unsigned longs, the destination bitmap
+ * @buf: array of u32 (in host byte order), the source bitmap
+ * @nbits: number of bits in @bitmap
+ */
+void bitmap_from_arr32(unsigned long *bitmap, const u32 *buf, unsigned int nbits)
+{
+ unsigned int i, halfwords;
+
+ halfwords = DIV_ROUND_UP(nbits, 32);
+ for (i = 0; i < halfwords; i++) {
+ bitmap[i/2] = (unsigned long) buf[i];
+ if (++i < halfwords)
+ bitmap[i/2] |= ((unsigned long) buf[i]) << 32;
+ }
+
+ /* Clear tail bits in last word beyond nbits. */
+ if (nbits % BITS_PER_LONG)
+ bitmap[(halfwords - 1) / 2] &= BITMAP_LAST_WORD_MASK(nbits);
+}
+EXPORT_SYMBOL(bitmap_from_arr32);
+
+/**
+ * bitmap_to_arr32 - copy the contents of bitmap to a u32 array of bits
+ * @buf: array of u32 (in host byte order), the dest bitmap
+ * @bitmap: array of unsigned longs, the source bitmap
+ * @nbits: number of bits in @bitmap
+ */
+void bitmap_to_arr32(u32 *buf, const unsigned long *bitmap, unsigned int nbits)
+{
+ unsigned int i, halfwords;
+
+ halfwords = DIV_ROUND_UP(nbits, 32);
+ for (i = 0; i < halfwords; i++) {
+ buf[i] = (u32) (bitmap[i/2] & UINT_MAX);
+ if (++i < halfwords)
+ buf[i] = (u32) (bitmap[i/2] >> 32);
+ }
+
+ /* Clear tail bits in last element of array beyond nbits. */
+ if (nbits % BITS_PER_LONG)
+ buf[halfwords - 1] &= (u32) (UINT_MAX >> ((-nbits) & 31));
+}
+EXPORT_SYMBOL(bitmap_to_arr32);
+#endif
+
+#if (BITS_PER_LONG == 32) && defined(__BIG_ENDIAN)
+/**
+ * bitmap_from_arr64 - copy the contents of u64 array of bits to bitmap
+ * @bitmap: array of unsigned longs, the destination bitmap
+ * @buf: array of u64 (in host byte order), the source bitmap
+ * @nbits: number of bits in @bitmap
+ */
+void bitmap_from_arr64(unsigned long *bitmap, const u64 *buf, unsigned int nbits)
+{
+ int n;
+
+ for (n = nbits; n > 0; n -= 64) {
+ u64 val = *buf++;
+
+ *bitmap++ = val;
+ if (n > 32)
+ *bitmap++ = val >> 32;
+ }
+
+ /*
+ * Clear tail bits in the last word beyond nbits.
+ *
+ * Negative index is OK because here we point to the word next
+ * to the last word of the bitmap, except for nbits == 0, which
+ * is tested implicitly.
+ */
+ if (nbits % BITS_PER_LONG)
+ bitmap[-1] &= BITMAP_LAST_WORD_MASK(nbits);
+}
+EXPORT_SYMBOL(bitmap_from_arr64);
+
+/**
+ * bitmap_to_arr64 - copy the contents of bitmap to a u64 array of bits
+ * @buf: array of u64 (in host byte order), the dest bitmap
+ * @bitmap: array of unsigned longs, the source bitmap
+ * @nbits: number of bits in @bitmap
+ */
+void bitmap_to_arr64(u64 *buf, const unsigned long *bitmap, unsigned int nbits)
+{
+ const unsigned long *end = bitmap + BITS_TO_LONGS(nbits);
+
+ while (bitmap < end) {
+ *buf = *bitmap++;
+ if (bitmap < end)
+ *buf |= (u64)(*bitmap++) << 32;
+ buf++;
+ }
+
+ /* Clear tail bits in the last element of array beyond nbits. */
+ if (nbits % 64)
+ buf[-1] &= GENMASK_ULL((nbits - 1) % 64, 0);
+}
+EXPORT_SYMBOL(bitmap_to_arr64);
+#endif
diff --git a/lib/blobgen.c b/lib/blobgen.c
index 5a556a68ce..0a4e192a27 100644
--- a/lib/blobgen.c
+++ b/lib/blobgen.c
@@ -29,7 +29,7 @@ static struct blobgen *bg_default;
* This registers a blob device. Returns 0 for success or a negative error
* code otherwise.
*/
-int blob_gen_register(struct device_d *dev, struct blobgen *bg)
+int blob_gen_register(struct device *dev, struct blobgen *bg)
{
int ret;
@@ -56,7 +56,7 @@ int blob_gen_register(struct device_d *dev, struct blobgen *bg)
*/
struct blobgen *blobgen_get(const char *name)
{
- struct device_d *dev;
+ struct device *dev;
struct blobgen *bg;
if (!name)
diff --git a/lib/bootstrap/Kconfig b/lib/bootstrap/Kconfig
index 645f0b4b78..5cd6035bee 100644
--- a/lib/bootstrap/Kconfig
+++ b/lib/bootstrap/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
menuconfig BOOTSTRAP
bool "Library bootstrap routines"
depends on SHELL_NONE
diff --git a/lib/bootstrap/Makefile b/lib/bootstrap/Makefile
index cbaa49f9d9..295d63e074 100644
--- a/lib/bootstrap/Makefile
+++ b/lib/bootstrap/Makefile
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-y += common.o
obj-$(CONFIG_BOOTSTRAP_DEVFS) += devfs.o
obj-$(CONFIG_BOOTSTRAP_DISK) += disk.o
diff --git a/lib/bootstrap/devfs.c b/lib/bootstrap/devfs.c
index 6a3dd76cdd..b023da1f94 100644
--- a/lib/bootstrap/devfs.c
+++ b/lib/bootstrap/devfs.c
@@ -6,7 +6,6 @@
*/
#include <common.h>
-#include <partition.h>
#include <nand.h>
#include <driver.h>
#include <linux/mtd/mtd.h>
@@ -32,7 +31,7 @@ static void *read_image_head(const char *name)
struct cdev *cdev;
int ret;
- cdev = cdev_open(name, O_RDONLY);
+ cdev = cdev_open_by_name(name, O_RDONLY);
if (!cdev) {
bootstrap_err("failed to open partition\n");
goto free_header;
@@ -80,8 +79,8 @@ static unsigned int get_image_size(void *head)
}
#endif
-void* bootstrap_read_devfs(const char *devname, bool use_bb, int offset,
- int default_size, int max_size)
+void* bootstrap_read_devfs(char *devname, bool use_bb, int offset,
+ int default_size, int max_size, size_t *bufsize)
{
int ret;
int size = 0;
@@ -89,8 +88,7 @@ void* bootstrap_read_devfs(const char *devname, bool use_bb, int offset,
struct cdev *cdev, *partition;
char *partname = "x";
- partition = devfs_add_partition(devname, offset, max_size,
- DEVFS_PARTITION_FIXED, partname);
+ partition = devfs_add_partition(devname, offset, max_size, 0, partname);
if (IS_ERR(partition)) {
bootstrap_err("%s: failed to add partition (%ld)\n",
devname, PTR_ERR(partition));
@@ -124,7 +122,7 @@ void* bootstrap_read_devfs(const char *devname, bool use_bb, int offset,
to = xmalloc(size);
- cdev = cdev_open(partname, O_RDONLY);
+ cdev = cdev_open_by_name(partname, O_RDONLY);
if (!cdev) {
bootstrap_err("%s: failed to open %s\n", devname, partname);
goto free_memory;
@@ -133,10 +131,13 @@ void* bootstrap_read_devfs(const char *devname, bool use_bb, int offset,
ret = cdev_read(cdev, to, size, 0, 0);
cdev_close(cdev);
- if (ret != size)
+ if (ret != size) {
bootstrap_err("%s: failed to read from %s\n", devname, partname);
- else
+ } else {
result = to;
+ if (bufsize)
+ *bufsize = size;
+ }
free_memory:
free(header);
@@ -149,7 +150,7 @@ free_memory:
}
delete_devfs_partition:
- devfs_del_partition(partname);
+ cdevfs_del_partition(partition);
return result;
}
diff --git a/lib/bootstrap/disk.c b/lib/bootstrap/disk.c
index fd016166e6..ed8b1aa407 100644
--- a/lib/bootstrap/disk.c
+++ b/lib/bootstrap/disk.c
@@ -14,7 +14,7 @@
#include <libfile.h>
#include <bootstrap.h>
-void* bootstrap_read_disk(const char *dev, const char *fstype)
+void* bootstrap_read_disk(const char *dev, char *fstype, size_t *bufsize)
{
int ret;
void *buf;
@@ -34,5 +34,8 @@ void* bootstrap_read_disk(const char *dev, const char *fstype)
return NULL;
}
+ if (bufsize)
+ *bufsize = len;
+
return buf;
}
diff --git a/lib/bsearch.c b/lib/bsearch.c
new file mode 100644
index 0000000000..3b78cd31f4
--- /dev/null
+++ b/lib/bsearch.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * A generic implementation of binary search for the Linux kernel
+ *
+ * Copyright (C) 2008-2009 Ksplice, Inc.
+ * Author: Tim Abbott <tabbott@ksplice.com>
+ */
+
+#include <module.h>
+#include <linux/bsearch.h>
+
+/*
+ * bsearch - binary search an array of elements
+ * @key: pointer to item being searched for
+ * @base: pointer to first element to search
+ * @num: number of elements
+ * @size: size of each element
+ * @cmp: pointer to comparison function
+ *
+ * This function does a binary search on the given array. The
+ * contents of the array should already be in ascending sorted order
+ * under the provided comparison function.
+ *
+ * Note that the key need not have the same type as the elements in
+ * the array, e.g. key could be a string and the comparison function
+ * could compare the string with the struct's name field. However, if
+ * the key and elements in the array are of the same type, you can use
+ * the same comparison function for both sort() and bsearch().
+ */
+void *bsearch(const void *key, const void *base, size_t num, size_t size, cmp_func_t cmp)
+{
+ return __inline_bsearch(key, base, num, size, cmp);
+}
+EXPORT_SYMBOL(bsearch);
diff --git a/lib/cmdlinepart.c b/lib/cmdlinepart.c
index 5a16462874..f1bfda641c 100644
--- a/lib/cmdlinepart.c
+++ b/lib/cmdlinepart.c
@@ -29,8 +29,7 @@ int cmdlinepart_do_parse_one(const char *devname, const char *partstr,
loff_t size;
char *end;
char buf[PATH_MAX] = {};
- unsigned long flags = 0;
- int ret = 0;
+ unsigned long flags = DEVFS_PARTITION_FOR_FIXUP;
struct cdev *cdev;
memset(buf, 0, PATH_MAX);
@@ -85,11 +84,11 @@ int cmdlinepart_do_parse_one(const char *devname, const char *partstr,
cdev = devfs_add_partition(devname, *offset, size, flags, buf);
if (IS_ERR(cdev)) {
- ret = PTR_ERR(cdev);
- printf("cannot create %s: %s\n", buf, strerror(-ret));
+ printf("cannot create %s: %pe\n", buf, cdev);
+ return PTR_ERR(cdev);
}
- return ret;
+ return 0;
}
int cmdlinepart_do_parse(const char *devname, const char *parts, loff_t devsize,
diff --git a/lib/decompress_bunzip2.c b/lib/decompress_bunzip2.c
index ee2862bebb..2daeb9b38c 100644
--- a/lib/decompress_bunzip2.c
+++ b/lib/decompress_bunzip2.c
@@ -89,7 +89,7 @@ struct bunzip_data {
/* State for interrupting output loop */
int writeCopies, writePos, writeRunCountdown, writeCount, writeCurrent;
/* I/O tracking data (file handles, buffers, positions, etc.) */
- int (*fill)(void*, unsigned int);
+ long (*fill)(void*, unsigned long);
int inbufCount, inbufPos /*, outbufPos*/;
unsigned char *inbuf /*,*outbuf*/;
unsigned int inbufBitCount, inbufBits;
@@ -614,7 +614,7 @@ decode_next_byte:
goto decode_next_byte;
}
-static int nofill(void *buf, unsigned int len)
+static long nofill(void *buf, unsigned long len)
{
return -1;
}
@@ -622,8 +622,8 @@ static int nofill(void *buf, unsigned int len)
/* Allocate the structure, read file header. If in_fd ==-1, inbuf must contain
a complete bunzip file (len bytes long). If in_fd!=-1, inbuf and len are
ignored, and data is read from file handle into temporary buffer. */
-static int start_bunzip(struct bunzip_data **bdp, void *inbuf, int len,
- int (*fill)(void*, unsigned int))
+static int start_bunzip(struct bunzip_data **bdp, void *inbuf, long len,
+ long (*fill)(void*, unsigned long))
{
struct bunzip_data *bd;
unsigned int i, j, c;
@@ -672,11 +672,11 @@ static int start_bunzip(struct bunzip_data **bdp, void *inbuf, int len,
/* Example usage: decompress src_fd to dst_fd. (Stops at end of bzip2 data,
not end of file.) */
-int bunzip2(unsigned char *buf, int len,
- int(*fill)(void*, unsigned int),
- int(*flush)(void*, unsigned int),
+int bunzip2(unsigned char *buf, long len,
+ long(*fill)(void*, unsigned long),
+ long(*flush)(void*, unsigned long),
unsigned char *outbuf,
- int *pos,
+ long *pos,
void(*error)(char *x))
{
struct bunzip_data *bd;
diff --git a/lib/decompress_inflate.c b/lib/decompress_inflate.c
index 47bd3db131..507190938c 100644
--- a/lib/decompress_inflate.c
+++ b/lib/decompress_inflate.c
@@ -32,17 +32,17 @@
#define GZIP_IOBUF_SIZE (16*1024)
-static int nofill(void *buffer, unsigned int len)
+static long nofill(void *buffer, unsigned long len)
{
return -1;
}
/* Included from initramfs et al code */
-int gunzip(unsigned char *buf, int len,
- int(*fill)(void*, unsigned int),
- int(*flush)(void*, unsigned int),
+int gunzip(unsigned char *buf, long len,
+ long(*fill)(void*, unsigned long),
+ long(*flush)(void*, unsigned long),
unsigned char *out_buf,
- int *pos,
+ long *pos,
void(*error)(char *x)) {
u8 *zbuf;
struct z_stream_s *strm;
diff --git a/lib/decompress_unlz4.c b/lib/decompress_unlz4.c
index 46a010ad4c..a18e6591e9 100644
--- a/lib/decompress_unlz4.c
+++ b/lib/decompress_unlz4.c
@@ -10,7 +10,6 @@
#ifdef STATIC
#define PREBOOT
-#include <linux/decompress/mm.h>
#include "lz4/lz4_decompress.c"
#else
#include <linux/decompress/unlz4.h>
@@ -39,10 +38,10 @@
#define LZ4_DEFAULT_UNCOMPRESSED_CHUNK_SIZE (8 << 20)
#define ARCHIVE_MAGICNUMBER 0x184C2102
-static inline int unlz4(u8 *input, int in_len,
- int (*fill) (void *, unsigned int),
- int (*flush) (void *, unsigned int),
- u8 *output, int *posp,
+static inline int unlz4(u8 *input, long in_len,
+ long (*fill) (void *, unsigned long),
+ long (*flush) (void *, unsigned long),
+ u8 *output, long *posp,
void (*error) (char *x))
{
int ret = -1;
@@ -181,11 +180,11 @@ exit_0:
return ret;
}
-STATIC int decompress_unlz4(unsigned char *buf, int in_len,
- int(*fill)(void*, unsigned int),
- int(*flush)(void*, unsigned int),
+STATIC int decompress_unlz4(unsigned char *buf, long in_len,
+ long(*fill)(void*, unsigned long),
+ long(*flush)(void*, unsigned long),
unsigned char *output,
- int *posp,
+ long *posp,
void(*error)(char *x)
)
{
diff --git a/lib/decompress_unlzo.c b/lib/decompress_unlzo.c
index ad7f977280..18168cb948 100644
--- a/lib/decompress_unlzo.c
+++ b/lib/decompress_unlzo.c
@@ -109,10 +109,10 @@ static inline int parse_header(u8 *input, int *skip, int in_len)
return 1;
}
-int decompress_unlzo(u8 *input, int in_len,
- int (*fill) (void *, unsigned int),
- int (*flush) (void *, unsigned int),
- u8 *output, int *posp,
+int decompress_unlzo(u8 *input, long in_len,
+ long (*fill) (void *, unsigned long),
+ long (*flush) (void *, unsigned long),
+ u8 *output, long *posp,
void (*error) (char *x))
{
u8 r = 0;
diff --git a/lib/decompress_unxz.c b/lib/decompress_unxz.c
index a7e2d331ab..7b8a9cd331 100644
--- a/lib/decompress_unxz.c
+++ b/lib/decompress_unxz.c
@@ -131,7 +131,13 @@
# define XZ_DEC_POWERPC
#endif
#ifdef CONFIG_ARM
-# define XZ_DEC_ARM
+# ifdef CONFIG_CPU_64
+# define XZ_DEC_ARM64
+# elif defined CONFIG_THUMB2_BAREBOX
+# define XZ_DEC_ARMTHUMB
+# else
+# define XZ_DEC_ARM
+# endif
#endif
#ifdef CONFIG_IA64
# define XZ_DEC_IA64
@@ -229,10 +235,10 @@ static void memzero(void *buf, size_t size)
* both input and output buffers are available as a single chunk, i.e. when
* fill() and flush() won't be used.
*/
-STATIC int decompress_unxz(unsigned char *in, int in_size,
- int (*fill)(void *dest, unsigned int size),
- int (*flush)(void *src, unsigned int size),
- unsigned char *out, int *in_used,
+STATIC int decompress_unxz(unsigned char *in, long in_size,
+ long (*fill)(void *dest, unsigned long size),
+ long (*flush)(void *src, unsigned long size),
+ unsigned char *out, long *in_used,
void (*error)(char *x))
{
struct xz_buf b;
diff --git a/lib/decompress_unzstd.c b/lib/decompress_unzstd.c
new file mode 100644
index 0000000000..bf987a8ccf
--- /dev/null
+++ b/lib/decompress_unzstd.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Important notes about in-place decompression
+ *
+ * At least on x86, the kernel is decompressed in place: the compressed data
+ * is placed to the end of the output buffer, and the decompressor overwrites
+ * most of the compressed data. There must be enough safety margin to
+ * guarantee that the write position is always behind the read position.
+ *
+ * The safety margin for ZSTD with a 128 KB block size is calculated below.
+ * Note that the margin with ZSTD is bigger than with GZIP or XZ!
+ *
+ * The worst case for in-place decompression is that the beginning of
+ * the file is compressed extremely well, and the rest of the file is
+ * uncompressible. Thus, we must look for worst-case expansion when the
+ * compressor is encoding uncompressible data.
+ *
+ * The structure of the .zst file in case of a compressed kernel is as follows.
+ * Maximum sizes (as bytes) of the fields are in parenthesis.
+ *
+ * Frame Header: (18)
+ * Blocks: (N)
+ * Checksum: (4)
+ *
+ * The frame header and checksum overhead is at most 22 bytes.
+ *
+ * ZSTD stores the data in blocks. Each block has a header whose size is
+ * a 3 bytes. After the block header, there is up to 128 KB of payload.
+ * The maximum uncompressed size of the payload is 128 KB. The minimum
+ * uncompressed size of the payload is never less than the payload size
+ * (excluding the block header).
+ *
+ * The assumption, that the uncompressed size of the payload is never
+ * smaller than the payload itself, is valid only when talking about
+ * the payload as a whole. It is possible that the payload has parts where
+ * the decompressor consumes more input than it produces output. Calculating
+ * the worst case for this would be tricky. Instead of trying to do that,
+ * let's simply make sure that the decompressor never overwrites any bytes
+ * of the payload which it is currently reading.
+ *
+ * Now we have enough information to calculate the safety margin. We need
+ * - 22 bytes for the .zst file format headers;
+ * - 3 bytes per every 128 KiB of uncompressed size (one block header per
+ * block); and
+ * - 128 KiB (biggest possible zstd block size) to make sure that the
+ * decompressor never overwrites anything from the block it is currently
+ * reading.
+ *
+ * We get the following formula:
+ *
+ * safety_margin = 22 + uncompressed_size * 3 / 131072 + 131072
+ * <= 22 + (uncompressed_size >> 15) + 131072
+ */
+
+#ifndef STATIC
+#include <linux/decompress/unzstd.h>
+#endif
+
+#include <linux/decompress/mm.h>
+#include <linux/kernel.h>
+#include <linux/zstd.h>
+
+/* 128MB is the maximum window size supported by zstd. */
+#define ZSTD_WINDOWSIZE_MAX (1 << ZSTD_WINDOWLOG_MAX)
+/*
+ * Size of the input and output buffers in multi-call mode.
+ * Pick a larger size because it isn't used during kernel decompression,
+ * since that is single pass, and we have to allocate a large buffer for
+ * zstd's window anyway. The larger size speeds up initramfs decompression.
+ */
+#define ZSTD_IOBUF_SIZE (1 << 17)
+
+static int INIT handle_zstd_error(size_t ret, void (*error)(char *x))
+{
+ const int err = ZSTD_getErrorCode(ret);
+
+ if (!ZSTD_isError(ret))
+ return 0;
+
+ switch (err) {
+ case ZSTD_error_memory_allocation:
+ error("ZSTD decompressor ran out of memory");
+ break;
+ case ZSTD_error_prefix_unknown:
+ error("Input is not in the ZSTD format (wrong magic bytes)");
+ break;
+ case ZSTD_error_dstSize_tooSmall:
+ case ZSTD_error_corruption_detected:
+ case ZSTD_error_checksum_wrong:
+ error("ZSTD-compressed data is corrupt");
+ break;
+ default:
+ error("ZSTD-compressed data is probably corrupt");
+ break;
+ }
+ return -1;
+}
+
+/*
+ * Handle the case where we have the entire input and output in one segment.
+ * We can allocate less memory (no circular buffer for the sliding window),
+ * and avoid some memcpy() calls.
+ */
+static int INIT decompress_single(const u8 *in_buf, long in_len, u8 *out_buf,
+ long out_len, long *in_pos,
+ void (*error)(char *x))
+{
+ const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
+ void *wksp = large_malloc(wksp_size);
+ ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size);
+ int err;
+ size_t ret;
+
+ if (dctx == NULL) {
+ error("Out of memory while allocating ZSTD_DCtx");
+ err = -1;
+ goto out;
+ }
+ /*
+ * Find out how large the frame actually is, there may be junk at
+ * the end of the frame that ZSTD_decompressDCtx() can't handle.
+ */
+ ret = ZSTD_findFrameCompressedSize(in_buf, in_len);
+ err = handle_zstd_error(ret, error);
+ if (err)
+ goto out;
+ in_len = (long)ret;
+
+ ret = ZSTD_decompressDCtx(dctx, out_buf, out_len, in_buf, in_len);
+ err = handle_zstd_error(ret, error);
+ if (err)
+ goto out;
+
+ if (in_pos != NULL)
+ *in_pos = in_len;
+
+ err = 0;
+out:
+ if (wksp != NULL)
+ large_free(wksp);
+ return err;
+}
+
+static int INIT __unzstd(unsigned char *in_buf, long in_len,
+ long (*fill)(void*, unsigned long),
+ long (*flush)(void*, unsigned long),
+ unsigned char *out_buf, long out_len,
+ long *in_pos,
+ void (*error)(char *x))
+{
+ ZSTD_inBuffer in;
+ ZSTD_outBuffer out;
+ ZSTD_frameParams params;
+ void *in_allocated = NULL;
+ void *out_allocated = NULL;
+ void *wksp = NULL;
+ size_t wksp_size;
+ ZSTD_DStream *dstream;
+ int err;
+ size_t ret;
+
+ /*
+ * ZSTD decompression code won't be happy if the buffer size is so big
+ * that its end address overflows. When the size is not provided, make
+ * it as big as possible without having the end address overflow.
+ */
+ if (out_len == 0)
+ out_len = UINTPTR_MAX - (uintptr_t)out_buf;
+
+ if (fill == NULL && flush == NULL)
+ /*
+ * We can decompress faster and with less memory when we have a
+ * single chunk.
+ */
+ return decompress_single(in_buf, in_len, out_buf, out_len,
+ in_pos, error);
+
+ /*
+ * If in_buf is not provided, we must be using fill(), so allocate
+ * a large enough buffer. If it is provided, it must be at least
+ * ZSTD_IOBUF_SIZE large.
+ */
+ if (in_buf == NULL) {
+ in_allocated = large_malloc(ZSTD_IOBUF_SIZE);
+ if (in_allocated == NULL) {
+ error("Out of memory while allocating input buffer");
+ err = -1;
+ goto out;
+ }
+ in_buf = in_allocated;
+ in_len = 0;
+ }
+ /* Read the first chunk, since we need to decode the frame header. */
+ if (fill != NULL)
+ in_len = fill(in_buf, ZSTD_IOBUF_SIZE);
+ if (in_len < 0) {
+ error("ZSTD-compressed data is truncated");
+ err = -1;
+ goto out;
+ }
+ /* Set the first non-empty input buffer. */
+ in.src = in_buf;
+ in.pos = 0;
+ in.size = in_len;
+ /* Allocate the output buffer if we are using flush(). */
+ if (flush != NULL) {
+ out_allocated = large_malloc(ZSTD_IOBUF_SIZE);
+ if (out_allocated == NULL) {
+ error("Out of memory while allocating output buffer");
+ err = -1;
+ goto out;
+ }
+ out_buf = out_allocated;
+ out_len = ZSTD_IOBUF_SIZE;
+ }
+ /* Set the output buffer. */
+ out.dst = out_buf;
+ out.pos = 0;
+ out.size = out_len;
+
+ /*
+ * We need to know the window size to allocate the ZSTD_DStream.
+ * Since we are streaming, we need to allocate a buffer for the sliding
+ * window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX
+ * (8 MB), so it is important to use the actual value so as not to
+ * waste memory when it is smaller.
+ */
+ ret = ZSTD_getFrameParams(&params, in.src, in.size);
+ err = handle_zstd_error(ret, error);
+ if (err)
+ goto out;
+ if (ret != 0) {
+ error("ZSTD-compressed data has an incomplete frame header");
+ err = -1;
+ goto out;
+ }
+ if (params.windowSize > ZSTD_WINDOWSIZE_MAX) {
+ error("ZSTD-compressed data has too large a window size");
+ err = -1;
+ goto out;
+ }
+
+ /*
+ * Allocate the ZSTD_DStream now that we know how much memory is
+ * required.
+ */
+ wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize);
+ wksp = large_malloc(wksp_size);
+ dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size);
+ if (dstream == NULL) {
+ error("Out of memory while allocating ZSTD_DStream");
+ err = -1;
+ goto out;
+ }
+
+ /*
+ * Decompression loop:
+ * Read more data if necessary (error if no more data can be read).
+ * Call the decompression function, which returns 0 when finished.
+ * Flush any data produced if using flush().
+ */
+ if (in_pos != NULL)
+ *in_pos = 0;
+ do {
+ /*
+ * If we need to reload data, either we have fill() and can
+ * try to get more data, or we don't and the input is truncated.
+ */
+ if (in.pos == in.size) {
+ if (in_pos != NULL)
+ *in_pos += in.pos;
+ in_len = fill ? fill(in_buf, ZSTD_IOBUF_SIZE) : -1;
+ if (in_len < 0) {
+ error("ZSTD-compressed data is truncated");
+ err = -1;
+ goto out;
+ }
+ in.pos = 0;
+ in.size = in_len;
+ }
+ /* Returns zero when the frame is complete. */
+ ret = ZSTD_decompressStream(dstream, &out, &in);
+ err = handle_zstd_error(ret, error);
+ if (err)
+ goto out;
+ /* Flush all of the data produced if using flush(). */
+ if (flush != NULL && out.pos > 0) {
+ if (out.pos != flush(out.dst, out.pos)) {
+ error("Failed to flush()");
+ err = -1;
+ goto out;
+ }
+ out.pos = 0;
+ }
+ } while (ret != 0);
+
+ if (in_pos != NULL)
+ *in_pos += in.pos;
+
+ err = 0;
+out:
+ if (in_allocated != NULL)
+ large_free(in_allocated);
+ if (out_allocated != NULL)
+ large_free(out_allocated);
+ if (wksp != NULL)
+ large_free(wksp);
+ return err;
+}
+
+STATIC int INIT unzstd(unsigned char *buf, long len,
+ long (*fill)(void*, unsigned long),
+ long (*flush)(void*, unsigned long),
+ unsigned char *out_buf,
+ long *pos,
+ void (*error)(char *x))
+{
+ return __unzstd(buf, len, fill, flush, out_buf, 0, pos, error);
+}
diff --git a/lib/div64.c b/lib/div64.c
deleted file mode 100644
index 0196725ce9..0000000000
--- a/lib/div64.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
- *
- * Based on former do_div() implementation from asm-parisc/div64.h:
- * Copyright (C) 1999 Hewlett-Packard Co
- * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
- *
- *
- * Generic C version of 64bit/32bit division and modulo, with
- * 64bit result and 32bit remainder.
- *
- * The fast case for (n>>32 == 0) is handled inline by do_div().
- *
- * Code generated for this function might be very inefficient
- * for some CPUs. __div64_32() can be overridden by linking arch-specific
- * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S.
- */
-
-#include <linux/types.h>
-
-#include <asm-generic/div64.h>
-
-uint32_t __div64_32(uint64_t *n, uint32_t base)
-{
- uint64_t rem = *n;
- uint64_t b = base;
- uint64_t res, d = 1;
- uint32_t high = rem >> 32;
-
- /* Reduce the thing a bit first */
- res = 0;
- if (high >= base) {
- high /= base;
- res = (uint64_t) high << 32;
- rem -= (uint64_t) (high*base) << 32;
- }
-
- while ((int64_t)b > 0 && b < rem) {
- b = b+b;
- d = d+d;
- }
-
- do {
- if (rem >= b) {
- rem -= b;
- res += d;
- }
- b >>= 1;
- d >>= 1;
- } while (d);
-
- *n = res;
- return rem;
-}
diff --git a/lib/fdt.c b/lib/fdt.c
index 97f20069fc..c83f9b94d2 100644
--- a/lib/fdt.c
+++ b/lib/fdt.c
@@ -1,2 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt.c"
diff --git a/lib/fdt_empty_tree.c b/lib/fdt_empty_tree.c
index 5d30c58150..794fcb6cac 100644
--- a/lib/fdt_empty_tree.c
+++ b/lib/fdt_empty_tree.c
@@ -1,2 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt_empty_tree.c"
diff --git a/lib/fdt_ro.c b/lib/fdt_ro.c
index f73c04ea7b..b4bab9618e 100644
--- a/lib/fdt_ro.c
+++ b/lib/fdt_ro.c
@@ -1,2 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt_ro.c"
diff --git a/lib/fdt_rw.c b/lib/fdt_rw.c
index 0c1f0f4a4b..72b5df529d 100644
--- a/lib/fdt_rw.c
+++ b/lib/fdt_rw.c
@@ -1,2 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt_rw.c"
diff --git a/lib/fdt_strerror.c b/lib/fdt_strerror.c
index 8713e3ff47..25883d28d9 100644
--- a/lib/fdt_strerror.c
+++ b/lib/fdt_strerror.c
@@ -1,2 +1,3 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt_strerror.c"
diff --git a/lib/fdt_sw.c b/lib/fdt_sw.c
index 9ac7e50c76..3f8e3397a9 100644
--- a/lib/fdt_sw.c
+++ b/lib/fdt_sw.c
@@ -1,2 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt_sw.c"
diff --git a/lib/fdt_wip.c b/lib/fdt_wip.c
index 45b3fc3d3b..da120b6dc9 100644
--- a/lib/fdt_wip.c
+++ b/lib/fdt_wip.c
@@ -1,2 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
#include <linux/libfdt_env.h>
#include "../scripts/dtc/libfdt/fdt_wip.c"
diff --git a/lib/fnmatch.c b/lib/fnmatch.c
index 0ab530d3b1..ac87ba7621 100644
--- a/lib/fnmatch.c
+++ b/lib/fnmatch.c
@@ -1,50 +1,11 @@
-/* Copyright (C) 1991, 1992, 1993, 1996 Free Software Foundation, Inc.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with this library; see the file COPYING.LIB. If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA. */
-
-/* Enable GNU extensions in fnmatch.h. */
-#ifndef _GNU_SOURCE
-# define _GNU_SOURCE 1
-#endif
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: (C) 1991, 1992, 1993, 1996 Free Software Foundation, Inc.
#include <errno.h>
#include <fnmatch.h>
#include <linux/ctype.h>
-
-/* Comment out all this code if we are using the GNU C Library, and are not
- actually compiling the library itself. This code is part of the GNU C
- Library, but also included in many other GNU distributions. Compiling
- and linking in this code is a waste when using the GNU C library
- (especially if it is a shared library). Rather than having every GNU
- program understand `configure --with-gnu-libc' and omit the object files,
- it is simpler to just do this in the source for each such file. */
-
-# if defined (STDC_HEADERS) || !defined (isascii)
-# define ISASCII(c) 1
-# else
-# define ISASCII(c) isascii(c)
-# endif
-
-# define ISUPPER(c) (ISASCII (c) && isupper (c))
-
-
-# ifndef errno
-extern int errno;
-# endif
+# define ISUPPER(c) (isascii(c) && isupper(c))
/* Match STRING against the filename pattern PATTERN, returning zero if
it matches, nonzero if not. */
diff --git a/lib/fonts/Kconfig b/lib/fonts/Kconfig
index e1a736d137..3201364cae 100644
--- a/lib/fonts/Kconfig
+++ b/lib/fonts/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
#
# Font configuration
#
diff --git a/lib/fonts/Makefile b/lib/fonts/Makefile
index 394e2c5e4c..1eae8ee67b 100644
--- a/lib/fonts/Makefile
+++ b/lib/fonts/Makefile
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
# Font handling
font-objs := fonts.o
diff --git a/lib/fonts/font_6x8.c b/lib/fonts/font_6x8.c
index a5efeee616..2b9061cbd0 100644
--- a/lib/fonts/font_6x8.c
+++ b/lib/fonts/font_6x8.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <init.h>
#include <linux/font.h>
diff --git a/lib/fonts/font_7x14.c b/lib/fonts/font_7x14.c
index e5ef8323ca..13b3897d8c 100644
--- a/lib/fonts/font_7x14.c
+++ b/lib/fonts/font_7x14.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/**************************************/
/* this file adapted from font_8x16.c */
/* by Jurriaan Kalkman 05-2005 */
diff --git a/lib/fonts/font_8x16.c b/lib/fonts/font_8x16.c
index e6d40d0de3..dbebf07e27 100644
--- a/lib/fonts/font_8x16.c
+++ b/lib/fonts/font_8x16.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/**********************************************/
/* */
/* Font file generated by cpi2fnt */
diff --git a/lib/fonts/font_8x8.c b/lib/fonts/font_8x8.c
index 27d53de578..40f1112427 100644
--- a/lib/fonts/font_8x8.c
+++ b/lib/fonts/font_8x8.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/**********************************************/
/* */
/* Font file generated by cpi2fnt */
diff --git a/lib/fonts/font_custom_16x.c b/lib/fonts/font_custom_16x.c
index 2666e1f6d6..eb21a81360 100644
--- a/lib/fonts/font_custom_16x.c
+++ b/lib/fonts/font_custom_16x.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/*
* by Du Huanpeng <u74147@gmail.com>
*/
diff --git a/lib/fonts/font_mini_4x6.c b/lib/fonts/font_mini_4x6.c
index 4a1de138a9..560a3e1c3a 100644
--- a/lib/fonts/font_mini_4x6.c
+++ b/lib/fonts/font_mini_4x6.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/* Hand composed "Minuscule" 4x6 font, with binary data generated using
* Perl stub.
diff --git a/lib/fonts/fonts.c b/lib/fonts/fonts.c
index 926f880128..e08b75fbcc 100644
--- a/lib/fonts/fonts.c
+++ b/lib/fonts/fonts.c
@@ -69,10 +69,10 @@ const struct font_desc *find_font_enum(int n)
return NULL;
}
-struct param_d *add_param_font(struct device_d *dev,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- int *value, void *priv)
+struct param_d *add_param_font(struct device *dev,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ int *value, void *priv)
{
struct font_desc *f;
int num_fonts = 0;
diff --git a/lib/gcd.c b/lib/gcd.c
index 86bdba9112..d895c1f12d 100644
--- a/lib/gcd.c
+++ b/lib/gcd.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <linux/gcd.h>
/* Greatest common divisor */
diff --git a/lib/genalloc.c b/lib/genalloc.c
new file mode 100644
index 0000000000..2e9815c36b
--- /dev/null
+++ b/lib/genalloc.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2005 Jes Sorensen <jes@trained-monkey.org>
+/*
+ * Basic general purpose allocator for managing special purpose
+ * memory, for example, memory that is not managed by the regular
+ * kmalloc/kfree interface. Uses for this includes on-device special
+ * memory, uncached memory etc.
+ */
+
+#include <io.h>
+#include <linux/ioport.h>
+#include <linux/genalloc.h>
+#include <linux/export.h>
+#include <of.h>
+#include <of_address.h>
+#include <driver.h>
+#include <linux/string.h>
+
+struct gen_pool {
+ struct resource res;
+};
+
+#define res_to_gen_pool(res) \
+ container_of(res, struct gen_pool, res)
+
+/**
+ * gen_pool_virt_to_phys - return the physical address of memory
+ * @pool: pool to allocate from
+ * @addr: starting address of memory
+ *
+ * Returns the physical address on success, or -1 on error.
+ */
+phys_addr_t gen_pool_virt_to_phys(struct gen_pool *pool, unsigned long addr)
+{
+ return virt_to_phys((void *)addr);
+}
+EXPORT_SYMBOL(gen_pool_virt_to_phys);
+
+/**
+ * gen_pool_dma_alloc - allocate special memory from the pool for DMA usage
+ * @pool: pool to allocate from
+ * @size: number of bytes to allocate from the pool
+ * @dma: dma-view physical address return value. Use %NULL if unneeded.
+ *
+ * Allocate the requested number of bytes from the specified pool.
+ * Uses the pool allocation function (with first-fit algorithm by default).
+ * Can not be used in NMI handler on architectures without
+ * NMI-safe cmpxchg implementation.
+ *
+ * Return: virtual address of the allocated memory, or %NULL on failure
+ */
+void *gen_pool_dma_alloc(struct gen_pool *pool, size_t size, dma_addr_t *dma)
+{
+ unsigned long vaddr;
+
+ if (!pool || resource_size(&pool->res) != size)
+ return NULL;
+
+ vaddr = pool->res.start;
+
+ if (dma)
+ *dma = gen_pool_virt_to_phys(pool, vaddr);
+
+ return (void *)vaddr;
+}
+EXPORT_SYMBOL(gen_pool_dma_alloc);
+
+/**
+ * gen_pool_dma_zalloc - allocate special zeroed memory from the pool for
+ * DMA usage
+ * @pool: pool to allocate from
+ * @size: number of bytes to allocate from the pool
+ * @dma: dma-view physical address return value. Use %NULL if unneeded.
+ *
+ * Return: virtual address of the allocated zeroed memory, or %NULL on failure
+ */
+void *gen_pool_dma_zalloc(struct gen_pool *pool, size_t size, dma_addr_t *dma)
+{
+ void *vaddr = gen_pool_dma_alloc(pool, size, dma);
+
+ if (vaddr)
+ memset(vaddr, 0, size);
+
+ return vaddr;
+}
+EXPORT_SYMBOL(gen_pool_dma_zalloc);
+
+#ifdef CONFIG_OFDEVICE
+/**
+ * of_gen_pool_get - find a pool by phandle property
+ * @np: device node
+ * @propname: property name containing phandle(s)
+ * @index: index into the phandle array
+ *
+ * Returns the pool that contains the chunk starting at the physical
+ * address of the device tree node pointed at by the phandle property,
+ * or NULL if not found.
+ */
+struct gen_pool *of_gen_pool_get(struct device_node *np,
+ const char *propname, int index)
+{
+ struct device_node *np_pool;
+ struct gen_pool gen_pool;
+ int ret;
+
+ np_pool = of_parse_phandle(np, propname, index);
+ if (!np_pool)
+ return NULL;
+
+ if (!of_device_is_compatible(np_pool, "mmio-sram"))
+ return NULL;
+
+ ret = of_address_to_resource(np_pool, 0, &gen_pool.res);
+ if (ret)
+ return NULL;
+
+ return memdup(&gen_pool, sizeof(gen_pool));
+}
+EXPORT_SYMBOL_GPL(of_gen_pool_get);
+#endif /* CONFIG_OF */
diff --git a/lib/getopt.c b/lib/getopt.c
index 55852ba133..356fc2ff4e 100644
--- a/lib/getopt.c
+++ b/lib/getopt.c
@@ -61,13 +61,24 @@ int getopt(int argc, char *argv[], const char *optstring)
{
char curopt; /* current option character */
const char *curoptp; /* pointer to the current option in optstring */
+ bool stop_nonopt = false;
+
+ if (*optstring == '+') {
+ stop_nonopt = true;
+ optstring++;
+ }
while(1) {
debug("optindex: %d nonopts: %d optind: %d\n", optindex, nonopts, optind);
- if (optindex == 1 && argv[optind] && !strcmp(argv[optind], "--")) {
- optind++;
- return -1;
+ if (optindex == 1 && argv[optind]) {
+ if (!strcmp(argv[optind], "--")) {
+ optind++;
+ return -1;
+ }
+
+ if (stop_nonopt && *argv[optind] != '-')
+ return -1;
}
/* first put nonopts to the end */
diff --git a/lib/glob.c b/lib/glob.c
index 32f7afdce8..389580b0ed 100644
--- a/lib/glob.c
+++ b/lib/glob.c
@@ -1,19 +1,5 @@
-/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
-
-This library is free software; you can redistribute it and/or
-modify it under the terms of the GNU Library General Public License as
-published by the Free Software Foundation; either version 2 of the
-License, or (at your option) any later version.
-
-This library is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-Library General Public License for more details.
-
-You should have received a copy of the GNU Library General Public
-License along with this library; see the file COPYING.LIB. If
-not, write to the Free Software Foundation, Inc., 675 Mass Ave,
-Cambridge, MA 02139, USA. */
+// SPDX-License-Identifier: LGPL-2.1-or-later
+// SPDX-FileCopyrightText: (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
#include <common.h>
#include <errno.h>
@@ -23,25 +9,15 @@ Cambridge, MA 02139, USA. */
#include <xfuncs.h>
#include <fnmatch.h>
#include <qsort.h>
-#define _GNU_SOURCE
#include <glob.h>
-#ifdef CONFIG_GLOB
-
-extern __ptr_t(*__glob_opendir_hook) __P((const char *directory));
-extern void (*__glob_closedir_hook) __P((__ptr_t stream));
-extern const char *(*__glob_readdir_hook) __P((__ptr_t stream));
-
-static int glob_in_dir __P((const char *pattern, const char *directory,
+static int glob_in_dir (const char *pattern, const char *directory,
int flags,
- int (*errfunc) __P((const char *, int)),
- glob_t * pglob));
-static int prefix_array __P((const char *prefix, char **array, size_t n,
- int add_slash));
-
-#ifdef __GLOB64
-extern int glob_pattern_p(const char *pattern, int quote);
-#else
+ int (*errfunc) (const char *, int),
+ glob_t * pglob);
+static int prefix_array (const char *prefix, char **array, size_t n,
+ int add_slash);
+
/* Return nonzero if PATTERN contains any metacharacters.
Metacharacters can be quoted with backslashes if QUOTE is nonzero. */
int glob_pattern_p(const char *pattern, int quote)
@@ -72,7 +48,6 @@ int glob_pattern_p(const char *pattern, int quote)
return 0;
}
-#endif
#ifdef CONFIG_GLOB_SORT
/* Do a collated comparison of A and B. */
@@ -100,7 +75,7 @@ static int collated_compare(const void *a, const void *b)
If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned.
Otherwise, `glob' returns zero. */
int glob(const char *pattern, int flags,
- int (*errfunc) __P((const char *, int)), glob_t *pglob)
+ int (*errfunc) (const char *, int), glob_t *pglob)
{
const char *filename;
char *dirname = NULL;
@@ -169,19 +144,6 @@ int glob(const char *pattern, int flags,
for (i = 0; i < dirs.gl_pathc; ++i) {
int oldcount1;
-#ifdef SHELL
- {
- /* Make globbing interruptible in the bash shell. */
- extern int interrupt_state;
-
- if (interrupt_state) {
- globfree(&dirs);
- globfree(&files);
- status = GLOB_ABEND goto out;
- }
- }
-#endif /* SHELL. */
-
oldcount1 = pglob->gl_pathc;
status = glob_in_dir(filename, dirs.gl_pathv[i],
(flags | GLOB_APPEND) &
@@ -263,7 +225,7 @@ int glob(const char *pattern, int flags,
#ifdef CONFIG_GLOB_SORT
if (!(flags & GLOB_NOSORT))
/* Sort the vector. */
- qsort((__ptr_t) & pglob->gl_pathv[oldcount],
+ qsort(&pglob->gl_pathv[oldcount],
pglob->gl_pathc - oldcount,
sizeof(char *), collated_compare);
#endif
@@ -299,7 +261,7 @@ static int prefix_array(const char *dirname, char **array, size_t n,
memcpy(new, dirname, dirlen);
new[dirlen] = '/';
memcpy(&new[dirlen + 1], array[i], eltlen);
- free((__ptr_t) array[i]);
+ free(array[i]);
array[i] = new;
}
@@ -311,9 +273,9 @@ static int prefix_array(const char *dirname, char **array, size_t n,
The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done.
The GLOB_APPEND flag is assumed to be set (always appends). */
static int glob_in_dir(const char *pattern, const char *directory,
- int flags, int (*errfunc) __P((const char *, int)), glob_t *pglob)
+ int flags, int (*errfunc) (const char *, int), glob_t *pglob)
{
- __ptr_t stream = NULL;
+ void *stream = NULL;
struct globlink {
struct globlink *next;
@@ -356,7 +318,7 @@ static int glob_in_dir(const char *pattern, const char *directory,
(struct globlink *)xmalloc(sizeof(struct globlink));
len = strlen(name);
new->name = xmalloc(len + ((flags & GLOB_MARK) ? 1 : 0) + 1);
- memcpy((__ptr_t) new->name, name, len);
+ memcpy(new->name, name, len);
new->name[len] = '\0';
new->next = names;
names = new;
@@ -406,43 +368,6 @@ static int glob_in_dir(const char *pattern, const char *directory,
}
return nfound == 0 ? GLOB_NOMATCH : 0;
}
-#endif /* CONFIG_GLOB */
-
-#ifdef CONFIG_FAKE_GLOB
-/* Fake version of glob. We simply put the input string into
- * the gl_pathv array. Currently we don't need it as hush.c won't
- * call us if no glob support is available.
- */
-int glob(pattern, flags, errfunc, pglob)
-const char *pattern;
-int flags;
-int (*errfunc) __P((const char *, int));
-glob_t *pglob;
-{
- int elems, i;
-
- if (!(flags & GLOB_APPEND)) {
- pglob->gl_pathc = 0;
- pglob->gl_pathv = NULL;
- }
-
- elems = pglob->gl_pathc + 2;
- if (flags & GLOB_DOOFFS)
- elems += pglob->gl_offs;
-
- pglob->gl_pathv = xrealloc(pglob->gl_pathv, elems * sizeof(char *));
-
- if (flags & GLOB_DOOFFS)
- for (i = 0; i < pglob->gl_offs; i++)
- pglob->gl_pathv[i] = NULL;
-
- pglob->gl_pathv[pglob->gl_pathc] = strdup(pattern);
- pglob->gl_pathc++;
- pglob->gl_pathv[pglob->gl_pathc] = NULL;
-
- return 0;
-}
-#endif /* CONFIG_FAKE_GLOB */
/* Free storage allocated in PGLOB by a previous `glob' call. */
void globfree(glob_t *pglob)
@@ -451,7 +376,7 @@ void globfree(glob_t *pglob)
int i = pglob->gl_flags & GLOB_DOOFFS ? pglob->gl_offs : 0;
for (; i < pglob->gl_pathc; ++i)
if (pglob->gl_pathv[i] != NULL)
- free((__ptr_t) pglob->gl_pathv[i]);
- free((__ptr_t) pglob->gl_pathv);
+ free(pglob->gl_pathv[i]);
+ free(pglob->gl_pathv);
}
}
diff --git a/lib/gui/2d-primitives.c b/lib/gui/2d-primitives.c
index 940e82b7d4..55dc1b1dda 100644
--- a/lib/gui/2d-primitives.c
+++ b/lib/gui/2d-primitives.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <fb.h>
#include <gui/graphic_utils.h>
diff --git a/lib/gui/Kconfig b/lib/gui/Kconfig
index 2625d9fb2f..ead1b4cfd2 100644
--- a/lib/gui/Kconfig
+++ b/lib/gui/Kconfig
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
menu "Library gui routines"
config IMAGE_RENDERER
@@ -16,6 +18,11 @@ config PNG
bool "png"
select ZLIB
+config QOI
+ bool "qoi"
+ help
+ Support for the Quite OK Image format
+
if PNG
choice
diff --git a/lib/gui/Makefile b/lib/gui/Makefile
index 31e66225d7..2dcd4d07ff 100644
--- a/lib/gui/Makefile
+++ b/lib/gui/Makefile
@@ -1,5 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-$(CONFIG_BMP) += bmp.o
obj-$(CONFIG_IMAGE_RENDERER) += image_renderer.o graphic_utils.o
+obj-$(CONFIG_QOI) += qoi.o
obj-$(CONFIG_PNG) += png.o
obj-$(CONFIG_LODEPNG) += png_lode.o lodepng.o
obj-$(CONFIG_PICOPNG) += png_pico.o picopng.o
diff --git a/lib/gui/bmp.c b/lib/gui/bmp.c
index 8bffc70a42..bbc623dba3 100644
--- a/lib/gui/bmp.c
+++ b/lib/gui/bmp.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <malloc.h>
diff --git a/lib/gui/graphic_utils.c b/lib/gui/graphic_utils.c
index 0bed932213..d91a7f3550 100644
--- a/lib/gui/graphic_utils.c
+++ b/lib/gui/graphic_utils.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <fb.h>
#include <gui/graphic_utils.h>
@@ -311,6 +313,12 @@ void gu_screen_blit_area(struct screen *sc, int startx, int starty, int width,
{
struct fb_info *info = sc->info;
int bpp = info->bits_per_pixel >> 3;
+ struct fb_rect rect = {
+ .x1 = startx,
+ .y1 = starty,
+ .x2 = startx + width,
+ .y2 = starty + height,
+ };
if (info->screen_base_shadow) {
int y;
@@ -323,14 +331,24 @@ void gu_screen_blit_area(struct screen *sc, int startx, int starty, int width,
fboff += sc->info->line_length;
}
}
+
+ fb_damage(info, &rect);
}
void gu_screen_blit(struct screen *sc)
{
struct fb_info *info = sc->info;
+ struct fb_rect rect = {
+ .x1 = 0,
+ .y1 = 0,
+ .x2 = info->xres,
+ .y2 = info->yres,
+ };
if (info->screen_base_shadow)
memcpy(info->screen_base, info->screen_base_shadow, sc->fbsize);
+
+ fb_damage(info, &rect);
}
void gu_fill_rectangle(struct screen *sc,
diff --git a/lib/gui/picopng.h b/lib/gui/picopng.h
index 14c5e28bcc..a17dd14b0c 100644
--- a/lib/gui/picopng.h
+++ b/lib/gui/picopng.h
@@ -1,3 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
#ifndef _PICOPNG_H
#define _PICOPNG_H
diff --git a/lib/gui/png.c b/lib/gui/png.c
index 6bf997c29a..01833485ce 100644
--- a/lib/gui/png.c
+++ b/lib/gui/png.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <malloc.h>
diff --git a/lib/gui/png_lode.c b/lib/gui/png_lode.c
index e30db0f853..68149c848d 100644
--- a/lib/gui/png_lode.c
+++ b/lib/gui/png_lode.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <malloc.h>
diff --git a/lib/gui/png_pico.c b/lib/gui/png_pico.c
index 256db35ce0..029fee2a40 100644
--- a/lib/gui/png_pico.c
+++ b/lib/gui/png_pico.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <errno.h>
#include <malloc.h>
diff --git a/lib/gui/qoi.c b/lib/gui/qoi.c
new file mode 100644
index 0000000000..b931868f16
--- /dev/null
+++ b/lib/gui/qoi.c
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later OR MIT
+
+#define pr_fmt(fmt) "qoi: " fmt
+
+#include <common.h>
+#include <errno.h>
+#include <malloc.h>
+#include <fb.h>
+#include <asm/byteorder.h>
+#include <gui/graphic_utils.h>
+#include <init.h>
+#include <gui/image_renderer.h>
+#include <asm/unaligned.h>
+
+#define QOI_NO_STDIO
+#define QOI_IMPLEMENTATION
+#include "qoi.h"
+
+static struct image *qoi_open(char *inbuf, int insize)
+{
+ struct image *img;
+ void *data;
+ qoi_desc qoi;
+
+ img = calloc(1, sizeof(*img));
+ if (!img)
+ return ERR_PTR(-ENOMEM);
+
+ data = qoi_decode(inbuf, insize, &qoi, 0);
+ if (!data) {
+ free(img);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ img->data = data;
+ img->height = qoi.height;
+ img->width = qoi.width;
+ img->bits_per_pixel = qoi.channels * 8;
+
+ pr_debug("%d x %d x %d data@0x%p\n", img->width, img->height,
+ img->bits_per_pixel, img->data);
+ return img;
+}
+
+static void qoi_close(struct image *img)
+{
+ free(img->data);
+}
+
+static int qoi_renderer(struct screen *sc, struct surface *s, struct image *img)
+{
+ int alpha = img->bits_per_pixel == (4 * 8);
+ int width = s->width;
+ int height = s->height;
+ int startx = s->x;
+ int starty = s->y;
+ void *buf;
+
+ if (s->width < 0)
+ width = img->width;
+ if (s->height < 0)
+ height = img->height;
+
+ if (startx < 0) {
+ startx = (sc->s.width - width) / 2;
+ if (startx < 0)
+ startx = 0;
+ }
+
+ if (starty < 0) {
+ starty = (sc->s.height - height) / 2;
+ if (starty < 0)
+ starty = 0;
+ }
+
+ width = min(width, sc->s.width - startx);
+ height = min(height, sc->s.height - starty);
+
+ buf = gui_screen_render_buffer(sc);
+
+ gu_rgba_blend(sc->info, img, buf, height, width, startx, starty, alpha);
+
+ return img->height;
+}
+
+static struct image_renderer qoi = {
+ .type = filetype_qoi,
+ .open = qoi_open,
+ .close = qoi_close,
+ .renderer = qoi_renderer,
+};
+
+static int qoi_init(void)
+{
+ return image_renderer_register(&qoi);
+}
+fs_initcall(qoi_init);
diff --git a/lib/gui/qoi.h b/lib/gui/qoi.h
new file mode 100644
index 0000000000..aea441434c
--- /dev/null
+++ b/lib/gui/qoi.h
@@ -0,0 +1,673 @@
+/* SPDX-License-Identifier: MIT */
+/* SPDX-FileCopyrightText: 2021 Dominic Szablewski */
+/*
+
+QOI - The "Quite OK Image" format for fast, lossless image compression
+
+Dominic Szablewski - https://phoboslab.org
+
+
+-- LICENSE: The MIT License(MIT)
+
+Copyright(c) 2021 Dominic Szablewski
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files(the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions :
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+
+-- About
+
+QOI encodes and decodes images in a lossless format. Compared to stb_image and
+stb_image_write QOI offers 20x-50x faster encoding, 3x-4x faster decoding and
+20% better compression.
+
+
+-- Synopsis
+
+// Define `QOI_IMPLEMENTATION` in *one* C/C++ file before including this
+// library to create the implementation.
+
+#define QOI_IMPLEMENTATION
+#include "qoi.h"
+
+// Encode and store an RGBA buffer to the file system. The qoi_desc describes
+// the input pixel data.
+qoi_write("image_new.qoi", rgba_pixels, &(qoi_desc){
+ .width = 1920,
+ .height = 1080,
+ .channels = 4,
+ .colorspace = QOI_SRGB
+});
+
+// Load and decode a QOI image from the file system into a 32bbp RGBA buffer.
+// The qoi_desc struct will be filled with the width, height, number of channels
+// and colorspace read from the file header.
+qoi_desc desc;
+void *rgba_pixels = qoi_read("image.qoi", &desc, 4);
+
+
+
+-- Documentation
+
+This library provides the following functions;
+- qoi_read -- read and decode a QOI file
+- qoi_decode -- decode the raw bytes of a QOI image from memory
+- qoi_write -- encode and write a QOI file
+- qoi_encode -- encode an rgba buffer into a QOI image in memory
+
+See the function declaration below for the signature and more information.
+
+If you don't want/need the qoi_read and qoi_write functions, you can define
+QOI_NO_STDIO before including this library.
+
+This library uses malloc() and free(). To supply your own malloc implementation
+you can define QOI_MALLOC and QOI_FREE before including this library.
+
+This library uses memset() to zero-initialize the index. To supply your own
+implementation you can define QOI_ZEROARR before including this library.
+
+
+-- Data Format
+
+A QOI file has a 14 byte header, followed by any number of data "chunks" and an
+8-byte end marker.
+
+struct qoi_header_t {
+ char magic[4]; // magic bytes "qoif"
+ uint32_t width; // image width in pixels (BE)
+ uint32_t height; // image height in pixels (BE)
+ uint8_t channels; // 3 = RGB, 4 = RGBA
+ uint8_t colorspace; // 0 = sRGB with linear alpha, 1 = all channels linear
+};
+
+Images are encoded row by row, left to right, top to bottom. The decoder and
+encoder start with {r: 0, g: 0, b: 0, a: 255} as the previous pixel value. An
+image is complete when all pixels specified by width * height have been covered.
+
+Pixels are encoded as
+ - a run of the previous pixel
+ - an index into an array of previously seen pixels
+ - a difference to the previous pixel value in r,g,b
+ - full r,g,b or r,g,b,a values
+
+The color channels are assumed to not be premultiplied with the alpha channel
+("un-premultiplied alpha").
+
+A running array[64] (zero-initialized) of previously seen pixel values is
+maintained by the encoder and decoder. Each pixel that is seen by the encoder
+and decoder is put into this array at the position formed by a hash function of
+the color value. In the encoder, if the pixel value at the index matches the
+current pixel, this index position is written to the stream as QOI_OP_INDEX.
+The hash function for the index is:
+
+ index_position = (r * 3 + g * 5 + b * 7 + a * 11) % 64
+
+Each chunk starts with a 2- or 8-bit tag, followed by a number of data bits. The
+bit length of chunks is divisible by 8 - i.e. all chunks are byte aligned. All
+values encoded in these data bits have the most significant bit on the left.
+
+The 8-bit tags have precedence over the 2-bit tags. A decoder must check for the
+presence of an 8-bit tag first.
+
+The byte stream's end is marked with 7 0x00 bytes followed a single 0x01 byte.
+
+
+The possible chunks are:
+
+
+.- QOI_OP_INDEX ----------.
+| Byte[0] |
+| 7 6 5 4 3 2 1 0 |
+|-------+-----------------|
+| 0 0 | index |
+`-------------------------`
+2-bit tag b00
+6-bit index into the color index array: 0..63
+
+A valid encoder must not issue 2 or more consecutive QOI_OP_INDEX chunks to the
+same index. QOI_OP_RUN should be used instead.
+
+
+.- QOI_OP_DIFF -----------.
+| Byte[0] |
+| 7 6 5 4 3 2 1 0 |
+|-------+-----+-----+-----|
+| 0 1 | dr | dg | db |
+`-------------------------`
+2-bit tag b01
+2-bit red channel difference from the previous pixel between -2..1
+2-bit green channel difference from the previous pixel between -2..1
+2-bit blue channel difference from the previous pixel between -2..1
+
+The difference to the current channel values are using a wraparound operation,
+so "1 - 2" will result in 255, while "255 + 1" will result in 0.
+
+Values are stored as unsigned integers with a bias of 2. E.g. -2 is stored as
+0 (b00). 1 is stored as 3 (b11).
+
+The alpha value remains unchanged from the previous pixel.
+
+
+.- QOI_OP_LUMA -------------------------------------.
+| Byte[0] | Byte[1] |
+| 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 |
+|-------+-----------------+-------------+-----------|
+| 1 0 | green diff | dr - dg | db - dg |
+`---------------------------------------------------`
+2-bit tag b10
+6-bit green channel difference from the previous pixel -32..31
+4-bit red channel difference minus green channel difference -8..7
+4-bit blue channel difference minus green channel difference -8..7
+
+The green channel is used to indicate the general direction of change and is
+encoded in 6 bits. The red and blue channels (dr and db) base their diffs off
+of the green channel difference and are encoded in 4 bits. I.e.:
+ dr_dg = (cur_px.r - prev_px.r) - (cur_px.g - prev_px.g)
+ db_dg = (cur_px.b - prev_px.b) - (cur_px.g - prev_px.g)
+
+The difference to the current channel values are using a wraparound operation,
+so "10 - 13" will result in 253, while "250 + 7" will result in 1.
+
+Values are stored as unsigned integers with a bias of 32 for the green channel
+and a bias of 8 for the red and blue channel.
+
+The alpha value remains unchanged from the previous pixel.
+
+
+.- QOI_OP_RUN ------------.
+| Byte[0] |
+| 7 6 5 4 3 2 1 0 |
+|-------+-----------------|
+| 1 1 | run |
+`-------------------------`
+2-bit tag b11
+6-bit run-length repeating the previous pixel: 1..62
+
+The run-length is stored with a bias of -1. Note that the run-lengths 63 and 64
+(b111110 and b111111) are illegal as they are occupied by the QOI_OP_RGB and
+QOI_OP_RGBA tags.
+
+
+.- QOI_OP_RGB ------------------------------------------.
+| Byte[0] | Byte[1] | Byte[2] | Byte[3] |
+| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
+|-------------------------+---------+---------+---------|
+| 1 1 1 1 1 1 1 0 | red | green | blue |
+`-------------------------------------------------------`
+8-bit tag b11111110
+8-bit red channel value
+8-bit green channel value
+8-bit blue channel value
+
+The alpha value remains unchanged from the previous pixel.
+
+
+.- QOI_OP_RGBA ---------------------------------------------------.
+| Byte[0] | Byte[1] | Byte[2] | Byte[3] | Byte[4] |
+| 7 6 5 4 3 2 1 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 | 7 .. 0 |
+|-------------------------+---------+---------+---------+---------|
+| 1 1 1 1 1 1 1 1 | red | green | blue | alpha |
+`-----------------------------------------------------------------`
+8-bit tag b11111111
+8-bit red channel value
+8-bit green channel value
+8-bit blue channel value
+8-bit alpha channel value
+
+*/
+
+
+/* -----------------------------------------------------------------------------
+Header - Public functions */
+
+#ifndef QOI_H
+#define QOI_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* A pointer to a qoi_desc struct has to be supplied to all of qoi's functions.
+It describes either the input format (for qoi_write and qoi_encode), or is
+filled with the description read from the file header (for qoi_read and
+qoi_decode).
+
+The colorspace in this qoi_desc is an enum where
+ 0 = sRGB, i.e. gamma scaled RGB channels and a linear alpha channel
+ 1 = all channels are linear
+You may use the constants QOI_SRGB or QOI_LINEAR. The colorspace is purely
+informative. It will be saved to the file header, but does not affect
+how chunks are en-/decoded. */
+
+#define QOI_SRGB 0
+#define QOI_LINEAR 1
+
+typedef struct {
+ unsigned int width;
+ unsigned int height;
+ unsigned char channels;
+ unsigned char colorspace;
+} qoi_desc;
+
+#ifndef QOI_NO_STDIO
+
+/* Encode raw RGB or RGBA pixels into a QOI image and write it to the file
+system. The qoi_desc struct must be filled with the image width, height,
+number of channels (3 = RGB, 4 = RGBA) and the colorspace.
+
+The function returns 0 on failure (invalid parameters, or fopen or malloc
+failed) or the number of bytes written on success. */
+
+int qoi_write(const char *filename, const void *data, const qoi_desc *desc);
+
+
+/* Read and decode a QOI image from the file system. If channels is 0, the
+number of channels from the file header is used. If channels is 3 or 4 the
+output format will be forced into this number of channels.
+
+The function either returns NULL on failure (invalid data, or malloc or fopen
+failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
+will be filled with the description from the file header.
+
+The returned pixel data should be free()d after use. */
+
+void *qoi_read(const char *filename, qoi_desc *desc, int channels);
+
+#endif /* QOI_NO_STDIO */
+
+
+/* Encode raw RGB or RGBA pixels into a QOI image in memory.
+
+The function either returns NULL on failure (invalid parameters or malloc
+failed) or a pointer to the encoded data on success. On success the out_len
+is set to the size in bytes of the encoded data.
+
+The returned qoi data should be free()d after use. */
+
+void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len);
+
+
+/* Decode a QOI image from memory.
+
+The function either returns NULL on failure (invalid parameters or malloc
+failed) or a pointer to the decoded pixels. On success, the qoi_desc struct
+is filled with the description from the file header.
+
+The returned pixel data should be free()d after use. */
+
+void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* QOI_H */
+
+
+/* -----------------------------------------------------------------------------
+Implementation */
+
+#ifdef QOI_IMPLEMENTATION
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef QOI_MALLOC
+ #define QOI_MALLOC(sz) malloc(sz)
+ #define QOI_FREE(p) free(p)
+#endif
+#ifndef QOI_ZEROARR
+ #define QOI_ZEROARR(a) memset((a),0,sizeof(a))
+#endif
+
+#define QOI_OP_INDEX 0x00 /* 00xxxxxx */
+#define QOI_OP_DIFF 0x40 /* 01xxxxxx */
+#define QOI_OP_LUMA 0x80 /* 10xxxxxx */
+#define QOI_OP_RUN 0xc0 /* 11xxxxxx */
+#define QOI_OP_RGB 0xfe /* 11111110 */
+#define QOI_OP_RGBA 0xff /* 11111111 */
+
+#define QOI_MASK_2 0xc0 /* 11000000 */
+
+#define QOI_COLOR_HASH(C) (C.rgba.r*3 + C.rgba.g*5 + C.rgba.b*7 + C.rgba.a*11)
+#define QOI_MAGIC \
+ (((unsigned int)'q') << 24 | ((unsigned int)'o') << 16 | \
+ ((unsigned int)'i') << 8 | ((unsigned int)'f'))
+#define QOI_HEADER_SIZE 14
+
+/* 2GB is the max file size that this implementation can safely handle. We guard
+against anything larger than that, assuming the worst case with 5 bytes per
+pixel, rounded down to a nice clean value. 400 million pixels ought to be
+enough for anybody. */
+#define QOI_PIXELS_MAX ((unsigned int)400000000)
+
+typedef union {
+ struct { unsigned char r, g, b, a; } rgba;
+ unsigned int v;
+} qoi_rgba_t;
+
+static const unsigned char qoi_padding[8] = {0,0,0,0,0,0,0,1};
+
+static void qoi_write_32(unsigned char *bytes, int *p, unsigned int v) {
+ bytes[(*p)++] = (0xff000000 & v) >> 24;
+ bytes[(*p)++] = (0x00ff0000 & v) >> 16;
+ bytes[(*p)++] = (0x0000ff00 & v) >> 8;
+ bytes[(*p)++] = (0x000000ff & v);
+}
+
+static unsigned int qoi_read_32(const unsigned char *bytes, int *p) {
+ unsigned int a = bytes[(*p)++];
+ unsigned int b = bytes[(*p)++];
+ unsigned int c = bytes[(*p)++];
+ unsigned int d = bytes[(*p)++];
+ return a << 24 | b << 16 | c << 8 | d;
+}
+
+void *qoi_encode(const void *data, const qoi_desc *desc, int *out_len) {
+ int i, max_size, p, run;
+ int px_len, px_end, px_pos, channels;
+ unsigned char *bytes;
+ const unsigned char *pixels;
+ qoi_rgba_t index[64];
+ qoi_rgba_t px, px_prev;
+
+ if (
+ data == NULL || out_len == NULL || desc == NULL ||
+ desc->width == 0 || desc->height == 0 ||
+ desc->channels < 3 || desc->channels > 4 ||
+ desc->colorspace > 1 ||
+ desc->height >= QOI_PIXELS_MAX / desc->width
+ ) {
+ return NULL;
+ }
+
+ max_size =
+ desc->width * desc->height * (desc->channels + 1) +
+ QOI_HEADER_SIZE + sizeof(qoi_padding);
+
+ p = 0;
+ bytes = (unsigned char *) QOI_MALLOC(max_size);
+ if (!bytes) {
+ return NULL;
+ }
+
+ qoi_write_32(bytes, &p, QOI_MAGIC);
+ qoi_write_32(bytes, &p, desc->width);
+ qoi_write_32(bytes, &p, desc->height);
+ bytes[p++] = desc->channels;
+ bytes[p++] = desc->colorspace;
+
+
+ pixels = (const unsigned char *)data;
+
+ QOI_ZEROARR(index);
+
+ run = 0;
+ px_prev.rgba.r = 0;
+ px_prev.rgba.g = 0;
+ px_prev.rgba.b = 0;
+ px_prev.rgba.a = 255;
+ px = px_prev;
+
+ px_len = desc->width * desc->height * desc->channels;
+ px_end = px_len - desc->channels;
+ channels = desc->channels;
+
+ for (px_pos = 0; px_pos < px_len; px_pos += channels) {
+ if (channels == 4) {
+ px = *(qoi_rgba_t *)(pixels + px_pos);
+ }
+ else {
+ px.rgba.r = pixels[px_pos + 0];
+ px.rgba.g = pixels[px_pos + 1];
+ px.rgba.b = pixels[px_pos + 2];
+ }
+
+ if (px.v == px_prev.v) {
+ run++;
+ if (run == 62 || px_pos == px_end) {
+ bytes[p++] = QOI_OP_RUN | (run - 1);
+ run = 0;
+ }
+ }
+ else {
+ int index_pos;
+
+ if (run > 0) {
+ bytes[p++] = QOI_OP_RUN | (run - 1);
+ run = 0;
+ }
+
+ index_pos = QOI_COLOR_HASH(px) % 64;
+
+ if (index[index_pos].v == px.v) {
+ bytes[p++] = QOI_OP_INDEX | index_pos;
+ }
+ else {
+ index[index_pos] = px;
+
+ if (px.rgba.a == px_prev.rgba.a) {
+ signed char vr = px.rgba.r - px_prev.rgba.r;
+ signed char vg = px.rgba.g - px_prev.rgba.g;
+ signed char vb = px.rgba.b - px_prev.rgba.b;
+
+ signed char vg_r = vr - vg;
+ signed char vg_b = vb - vg;
+
+ if (
+ vr > -3 && vr < 2 &&
+ vg > -3 && vg < 2 &&
+ vb > -3 && vb < 2
+ ) {
+ bytes[p++] = QOI_OP_DIFF | (vr + 2) << 4 | (vg + 2) << 2 | (vb + 2);
+ }
+ else if (
+ vg_r > -9 && vg_r < 8 &&
+ vg > -33 && vg < 32 &&
+ vg_b > -9 && vg_b < 8
+ ) {
+ bytes[p++] = QOI_OP_LUMA | (vg + 32);
+ bytes[p++] = (vg_r + 8) << 4 | (vg_b + 8);
+ }
+ else {
+ bytes[p++] = QOI_OP_RGB;
+ bytes[p++] = px.rgba.r;
+ bytes[p++] = px.rgba.g;
+ bytes[p++] = px.rgba.b;
+ }
+ }
+ else {
+ bytes[p++] = QOI_OP_RGBA;
+ bytes[p++] = px.rgba.r;
+ bytes[p++] = px.rgba.g;
+ bytes[p++] = px.rgba.b;
+ bytes[p++] = px.rgba.a;
+ }
+ }
+ }
+ px_prev = px;
+ }
+
+ for (i = 0; i < (int)sizeof(qoi_padding); i++) {
+ bytes[p++] = qoi_padding[i];
+ }
+
+ *out_len = p;
+ return bytes;
+}
+
+void *qoi_decode(const void *data, int size, qoi_desc *desc, int channels) {
+ const unsigned char *bytes;
+ unsigned int header_magic;
+ unsigned char *pixels;
+ qoi_rgba_t index[64];
+ qoi_rgba_t px;
+ int px_len, chunks_len, px_pos;
+ int p = 0, run = 0;
+
+ if (
+ data == NULL || desc == NULL ||
+ (channels != 0 && channels != 3 && channels != 4) ||
+ size < QOI_HEADER_SIZE + (int)sizeof(qoi_padding)
+ ) {
+ return NULL;
+ }
+
+ bytes = (const unsigned char *)data;
+
+ header_magic = qoi_read_32(bytes, &p);
+ desc->width = qoi_read_32(bytes, &p);
+ desc->height = qoi_read_32(bytes, &p);
+ desc->channels = bytes[p++];
+ desc->colorspace = bytes[p++];
+
+ if (
+ desc->width == 0 || desc->height == 0 ||
+ desc->channels < 3 || desc->channels > 4 ||
+ desc->colorspace > 1 ||
+ header_magic != QOI_MAGIC ||
+ desc->height >= QOI_PIXELS_MAX / desc->width
+ ) {
+ return NULL;
+ }
+
+ if (channels == 0) {
+ channels = desc->channels;
+ }
+
+ px_len = desc->width * desc->height * channels;
+ pixels = (unsigned char *) QOI_MALLOC(px_len);
+ if (!pixels) {
+ return NULL;
+ }
+
+ QOI_ZEROARR(index);
+ px.rgba.r = 0;
+ px.rgba.g = 0;
+ px.rgba.b = 0;
+ px.rgba.a = 255;
+
+ chunks_len = size - (int)sizeof(qoi_padding);
+ for (px_pos = 0; px_pos < px_len; px_pos += channels) {
+ if (run > 0) {
+ run--;
+ }
+ else if (p < chunks_len) {
+ int b1 = bytes[p++];
+
+ if (b1 == QOI_OP_RGB) {
+ px.rgba.r = bytes[p++];
+ px.rgba.g = bytes[p++];
+ px.rgba.b = bytes[p++];
+ }
+ else if (b1 == QOI_OP_RGBA) {
+ px.rgba.r = bytes[p++];
+ px.rgba.g = bytes[p++];
+ px.rgba.b = bytes[p++];
+ px.rgba.a = bytes[p++];
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_INDEX) {
+ px = index[b1];
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_DIFF) {
+ px.rgba.r += ((b1 >> 4) & 0x03) - 2;
+ px.rgba.g += ((b1 >> 2) & 0x03) - 2;
+ px.rgba.b += ( b1 & 0x03) - 2;
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_LUMA) {
+ int b2 = bytes[p++];
+ int vg = (b1 & 0x3f) - 32;
+ px.rgba.r += vg - 8 + ((b2 >> 4) & 0x0f);
+ px.rgba.g += vg;
+ px.rgba.b += vg - 8 + (b2 & 0x0f);
+ }
+ else if ((b1 & QOI_MASK_2) == QOI_OP_RUN) {
+ run = (b1 & 0x3f);
+ }
+
+ index[QOI_COLOR_HASH(px) % 64] = px;
+ }
+
+ if (channels == 4) {
+ *(qoi_rgba_t*)(pixels + px_pos) = px;
+ }
+ else {
+ pixels[px_pos + 0] = px.rgba.r;
+ pixels[px_pos + 1] = px.rgba.g;
+ pixels[px_pos + 2] = px.rgba.b;
+ }
+ }
+
+ return pixels;
+}
+
+#ifndef QOI_NO_STDIO
+#include <stdio.h>
+
+int qoi_write(const char *filename, const void *data, const qoi_desc *desc) {
+ FILE *f = fopen(filename, "wb");
+ int size;
+ void *encoded;
+
+ if (!f) {
+ return 0;
+ }
+
+ encoded = qoi_encode(data, desc, &size);
+ if (!encoded) {
+ fclose(f);
+ return 0;
+ }
+
+ fwrite(encoded, 1, size, f);
+ fclose(f);
+
+ QOI_FREE(encoded);
+ return size;
+}
+
+void *qoi_read(const char *filename, qoi_desc *desc, int channels) {
+ FILE *f = fopen(filename, "rb");
+ int size, bytes_read;
+ void *pixels, *data;
+
+ if (!f) {
+ return NULL;
+ }
+
+ fseek(f, 0, SEEK_END);
+ size = ftell(f);
+ if (size <= 0) {
+ fclose(f);
+ return NULL;
+ }
+ fseek(f, 0, SEEK_SET);
+
+ data = QOI_MALLOC(size);
+ if (!data) {
+ fclose(f);
+ return NULL;
+ }
+
+ bytes_read = fread(data, 1, size, f);
+ fclose(f);
+
+ pixels = qoi_decode(data, bytes_read, desc, channels);
+ QOI_FREE(data);
+ return pixels;
+}
+
+#endif /* QOI_NO_STDIO */
+#endif /* QOI_IMPLEMENTATION */
diff --git a/lib/hexdump.c b/lib/hexdump.c
index 3b1d5e6736..ae078536e3 100644
--- a/lib/hexdump.c
+++ b/lib/hexdump.c
@@ -1,15 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* lib/hexdump.c
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation. See README and COPYING for
- * more details.
*/
#include <common.h>
-#include <linux/kernel.h>
+#include <driver.h>
+#include <linux/types.h>
#include <linux/ctype.h>
+#include <linux/log2.h>
+#include <linux/printk.h>
+#include <asm/unaligned.h>
+#include <pbl.h>
const char hex_asc[] = "0123456789abcdef";
EXPORT_SYMBOL(hex_asc);
@@ -40,7 +41,7 @@ EXPORT_SYMBOL(hex_to_bin);
* @src: ascii hexadecimal string
* @count: result length
*
- * Return 0 on success, -1 in case of bad input.
+ * Return 0 on success, -EINVAL in case of bad input.
*/
int hex2bin(u8 *dst, const char *src, size_t count)
{
@@ -49,7 +50,7 @@ int hex2bin(u8 *dst, const char *src, size_t count)
int lo = hex_to_bin(*src++);
if ((hi < 0) || (lo < 0))
- return -1;
+ return -EINVAL;
*dst++ = (hi << 4) | lo;
}
@@ -72,3 +73,212 @@ char *bin2hex(char *dst, const void *src, size_t count)
return dst;
}
EXPORT_SYMBOL(bin2hex);
+
+/**
+ * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @linebuf: where to put the converted data
+ * @linebuflen: total size of @linebuf, including space for terminating NUL
+ * @ascii: include ASCII after the hex output
+ *
+ * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ *
+ * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data
+ * to a hex + ASCII dump at the supplied memory location.
+ * The converted output is always NUL-terminated.
+ *
+ * E.g.:
+ * hex_dump_to_buffer(frame->data, frame->len, 16, 1,
+ * linebuf, sizeof(linebuf), true);
+ *
+ * example output buffer:
+ * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ *
+ * Return:
+ * The amount of bytes placed in the buffer without terminating NUL. If the
+ * output was truncated, then the return value is the number of bytes
+ * (excluding the terminating NUL) which would have been written to the final
+ * string if enough space had been available.
+ */
+int hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize,
+ char *linebuf, size_t linebuflen, bool ascii)
+{
+ const u8 *ptr = buf;
+ int ngroups;
+ u8 ch;
+ int j, lx = 0;
+ int ascii_column;
+ int ret;
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ if (len > rowsize) /* limit to one line at a time */
+ len = rowsize;
+ if (!is_power_of_2(groupsize) || groupsize > 8)
+ groupsize = 1;
+ if ((len % groupsize) != 0) /* no mixed size output */
+ groupsize = 1;
+
+ ngroups = len / groupsize;
+ ascii_column = rowsize * 2 + rowsize / groupsize + 1;
+
+ if (!linebuflen)
+ goto overflow1;
+
+ if (!len)
+ goto nil;
+
+ if (groupsize == 8) {
+ const u64 *ptr8 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%16.16llx", j ? " " : "",
+ get_unaligned(ptr8 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 4) {
+ const u32 *ptr4 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%8.8x", j ? " " : "",
+ get_unaligned(ptr4 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else if (groupsize == 2) {
+ const u16 *ptr2 = buf;
+
+ for (j = 0; j < ngroups; j++) {
+ ret = snprintf(linebuf + lx, linebuflen - lx,
+ "%s%4.4x", j ? " " : "",
+ get_unaligned(ptr2 + j));
+ if (ret >= linebuflen - lx)
+ goto overflow1;
+ lx += ret;
+ }
+ } else {
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = hex_asc_hi(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = hex_asc_lo(ch);
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ if (j)
+ lx--;
+ }
+ if (!ascii)
+ goto nil;
+
+ while (lx < ascii_column) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ linebuf[lx++] = ' ';
+ }
+ for (j = 0; j < len; j++) {
+ if (linebuflen < lx + 2)
+ goto overflow2;
+ ch = ptr[j];
+ linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
+ }
+nil:
+ linebuf[lx] = '\0';
+ return lx;
+overflow2:
+ linebuf[lx++] = '\0';
+overflow1:
+ return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1;
+}
+EXPORT_SYMBOL(hex_dump_to_buffer);
+
+/**
+ * print_hex_dump - print a text hex dump to syslog for a binary blob of data
+ * @level: kernel log level (e.g. KERN_DEBUG)
+ * @prefix_str: string to prefix each line with;
+ * caller supplies trailing spaces for alignment if desired
+ * @prefix_type: controls whether prefix of an offset, address, or none
+ * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
+ * @rowsize: number of bytes to print per line; must be 16 or 32
+ * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1)
+ * @buf: data blob to dump
+ * @len: number of bytes in the @buf
+ * @ascii: include ASCII after the hex output
+ *
+ * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump
+ * to the kernel log at the specified kernel log level, with an optional
+ * leading prefix.
+ *
+ * print_hex_dump() works on one "line" of output at a time, i.e.,
+ * 16 or 32 bytes of input data converted to hex + ASCII output.
+ * print_hex_dump() iterates over the entire input @buf, breaking it into
+ * "line size" chunks to format and print.
+ *
+ * E.g.:
+ * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
+ * 16, 1, frame->data, frame->len, true);
+ *
+ * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode:
+ * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO
+ * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode:
+ * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.
+ */
+void dev_print_hex_dump(struct device *dev, const char *level,
+ const char *prefix_str, int prefix_type, int rowsize,
+ int groupsize, const void *buf, size_t len, bool ascii)
+{
+ const u8 *ptr = buf;
+ int i, linelen, remaining = len;
+ unsigned char linebuf[32 * 3 + 2 + 32 + 1];
+ char *name = "";
+
+ if (IN_PBL)
+ dev = NULL;
+
+ if (dev)
+ name = basprintf("%s: ", dev_name(dev));
+
+ if (rowsize != 16 && rowsize != 32)
+ rowsize = 16;
+
+ for (i = 0; i < len; i += rowsize) {
+ linelen = min(remaining, rowsize);
+ remaining -= rowsize;
+
+ hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
+ linebuf, sizeof(linebuf), ascii);
+
+ switch (prefix_type) {
+ case DUMP_PREFIX_ADDRESS:
+ printk("%s%s%s%p: %s\n", level, name, prefix_str,
+ ptr + i, linebuf);
+ break;
+ case DUMP_PREFIX_OFFSET:
+ printk("%s%s%s%.8x: %s\n", level, name, prefix_str,
+ i, linebuf);
+ break;
+ default:
+ printk("%s%s%s%s\n", level, name, prefix_str,
+ linebuf);
+ break;
+ }
+ }
+
+ if (dev)
+ free(name);
+}
+EXPORT_SYMBOL(dev_print_hex_dump);
diff --git a/lib/idr.c b/lib/idr.c
new file mode 100644
index 0000000000..10a714ac03
--- /dev/null
+++ b/lib/idr.c
@@ -0,0 +1,100 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 2002-10-18 written by Jim Houston jim.houston@ccur.com
+ * Copyright (C) 2002 by Concurrent Computer Corporation
+ */
+
+#include <errno.h>
+#include <linux/idr.h>
+
+struct idr *__idr_find(struct idr *head, int lookup_id)
+{
+ struct idr *cursor;
+
+ list_for_each_entry(cursor, &head->list, list) {
+ if (cursor->id == lookup_id)
+ return cursor;
+ }
+
+ return NULL;
+}
+
+/**
+ * idr_for_each() - Iterate through all stored pointers.
+ * @idr: IDR handle.
+ * @fn: Function to be called for each pointer.
+ * @data: Data passed to callback function.
+ *
+ * The callback function will be called for each entry in @idr, passing
+ * the ID, the entry and @data.
+ *
+ * If @fn returns anything other than %0, the iteration stops and that
+ * value is returned from this function.
+ */
+int idr_for_each(const struct idr *idr,
+ int (*fn)(int id, void *p, void *data), void *data)
+{
+ const struct idr *pos, *tmp;
+ int ret;
+
+ list_for_each_entry_safe(pos, tmp, &idr->list, list) {
+ ret = fn(pos->id, pos->ptr, data);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int idr_compare(struct list_head *a, struct list_head *b)
+{
+ int id_a = list_entry(a, struct idr, list)->id;
+ int id_b = list_entry(b, struct idr, list)->id;
+
+ return __compare3(id_a, id_b);
+}
+
+int idr_alloc_one(struct idr *head, void *ptr, int start)
+{
+ struct idr *idr;
+
+ if (__idr_find(head, start))
+ return -EBUSY;
+
+ idr = malloc(sizeof(*idr));
+ if (!idr)
+ return -ENOMEM;
+
+ idr->id = start;
+ idr->ptr = ptr;
+
+ list_add_sort(&idr->list, &head->list, idr_compare);
+
+ return start;
+}
+
+static void __idr_remove(struct idr *idr)
+{
+ list_del(&idr->list);
+ free(idr);
+}
+
+void idr_remove(struct idr *head, int id)
+{
+ struct idr *idr = __idr_find(head, id);
+ if (!idr)
+ return;
+
+ __idr_remove(idr);
+}
+
+void idr_destroy(struct idr *idr)
+{
+ struct idr *pos, *tmp;
+
+ if (!idr)
+ return;
+
+ list_for_each_entry_safe(pos, tmp, &idr->list, list)
+ __idr_remove(pos);
+}
diff --git a/lib/image-sparse.c b/lib/image-sparse.c
index 0c31742ab6..eb5242e25a 100644
--- a/lib/image-sparse.c
+++ b/lib/image-sparse.c
@@ -46,10 +46,6 @@
#include <linux/math64.h>
-#ifndef CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE
-#define CONFIG_FASTBOOT_FLASH_FILLBUF_SIZE (1024 * 512)
-#endif
-
struct sparse_image_ctx {
int fd;
struct sparse_header sparse;
@@ -62,7 +58,8 @@ struct sparse_image_ctx {
static int sparse_seek(struct sparse_image_ctx *si)
{
- unsigned int chunk_data_sz, payload;
+ uint64_t chunk_data_sz;
+ unsigned int payload;
loff_t offs;
int ret;
@@ -94,7 +91,7 @@ again:
return -errno;
}
- chunk_data_sz = si->sparse.blk_sz * si->chunk.chunk_sz;
+ chunk_data_sz = (uint64_t) si->sparse.blk_sz * si->chunk.chunk_sz;
payload = si->chunk.total_sz - si->sparse.chunk_hdr_sz;
si->processed_chunks++;
@@ -190,7 +187,7 @@ out:
}
int sparse_image_read(struct sparse_image_ctx *si, void *buf, loff_t *pos,
- size_t len, int *retlen)
+ size_t len, size_t *retlen)
{
size_t now;
int ret, i;
diff --git a/lib/jsmn.c b/lib/jsmn.c
new file mode 100644
index 0000000000..10a77886a8
--- /dev/null
+++ b/lib/jsmn.c
@@ -0,0 +1,505 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Copyright (c) 2010 Serge Zaitsev
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <jsmn.h>
+
+/**
+ * Allocates a fresh unused token from the token pool.
+ */
+static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, jsmntok_t *tokens,
+ const size_t num_tokens) {
+ jsmntok_t *tok;
+ if (parser->toknext >= num_tokens) {
+ return NULL;
+ }
+ tok = &tokens[parser->toknext++];
+ tok->start = tok->end = -1;
+ tok->size = 0;
+#ifdef JSMN_PARENT_LINKS
+ tok->parent = -1;
+#endif
+ return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token(jsmntok_t *token, const jsmntype_t type,
+ const int start, const int end) {
+ token->type = type;
+ token->start = start;
+ token->end = end;
+ token->size = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive(jsmn_parser *parser, const char *js,
+ const size_t len, jsmntok_t *tokens,
+ const size_t num_tokens) {
+ jsmntok_t *token;
+ int start;
+
+ start = parser->pos;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ switch (js[parser->pos]) {
+#ifndef JSMN_STRICT
+ /* In strict mode primitive must be followed by "," or "}" or "]" */
+ case ':':
+#endif
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ case ',':
+ case ']':
+ case '}':
+ goto found;
+ default:
+ /* to quiet a warning from gcc*/
+ break;
+ }
+ if (js[parser->pos] < 32 || js[parser->pos] >= 127) {
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#ifdef JSMN_STRICT
+ /* In strict mode primitive must be followed by a comma/object/array */
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+#endif
+
+found:
+ if (tokens == NULL) {
+ parser->pos--;
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ parser->pos--;
+ return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string(jsmn_parser *parser, const char *js,
+ const size_t len, jsmntok_t *tokens,
+ const size_t num_tokens) {
+ jsmntok_t *token;
+
+ int start = parser->pos;
+
+ /* Skip starting quote */
+ parser->pos++;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c = js[parser->pos];
+
+ /* Quote: end of string */
+ if (c == '\"') {
+ if (tokens == NULL) {
+ return 0;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ parser->pos = start;
+ return JSMN_ERROR_NOMEM;
+ }
+ jsmn_fill_token(token, JSMN_STRING, start + 1, parser->pos);
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ return 0;
+ }
+
+ /* Backslash: Quoted symbol expected */
+ if (c == '\\' && parser->pos + 1 < len) {
+ int i;
+ parser->pos++;
+ switch (js[parser->pos]) {
+ /* Allowed escaped symbols */
+ case '\"':
+ case '/':
+ case '\\':
+ case 'b':
+ case 'f':
+ case 'r':
+ case 'n':
+ case 't':
+ break;
+ /* Allows escaped symbol \uXXXX */
+ case 'u':
+ parser->pos++;
+ for (i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0';
+ i++) {
+ /* If it isn't a hex character we have an error */
+ if (!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */
+ (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */
+ (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ parser->pos++;
+ }
+ parser->pos--;
+ break;
+ /* Unexpected symbol */
+ default:
+ parser->pos = start;
+ return JSMN_ERROR_INVAL;
+ }
+ }
+ }
+ parser->pos = start;
+ return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+JSMN_API int jsmn_parse(jsmn_parser *parser, const char *js, const size_t len,
+ jsmntok_t *tokens, const unsigned int num_tokens) {
+ int r;
+ int i;
+ jsmntok_t *token;
+ int count = parser->toknext;
+
+ for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) {
+ char c;
+ jsmntype_t type;
+
+ c = js[parser->pos];
+ switch (c) {
+ case '{':
+ case '[':
+ count++;
+ if (tokens == NULL) {
+ break;
+ }
+ token = jsmn_alloc_token(parser, tokens, num_tokens);
+ if (token == NULL) {
+ return JSMN_ERROR_NOMEM;
+ }
+ if (parser->toksuper != -1) {
+ jsmntok_t *t = &tokens[parser->toksuper];
+#ifdef JSMN_STRICT
+ /* In strict mode an object or array can't become a key */
+ if (t->type == JSMN_OBJECT) {
+ return JSMN_ERROR_INVAL;
+ }
+#endif
+ t->size++;
+#ifdef JSMN_PARENT_LINKS
+ token->parent = parser->toksuper;
+#endif
+ }
+ token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY);
+ token->start = parser->pos;
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case '}':
+ case ']':
+ if (tokens == NULL) {
+ break;
+ }
+ type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY);
+#ifdef JSMN_PARENT_LINKS
+ if (parser->toknext < 1) {
+ return JSMN_ERROR_INVAL;
+ }
+ token = &tokens[parser->toknext - 1];
+ for (;;) {
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ token->end = parser->pos + 1;
+ parser->toksuper = token->parent;
+ break;
+ }
+ if (token->parent == -1) {
+ if (token->type != type || parser->toksuper == -1) {
+ return JSMN_ERROR_INVAL;
+ }
+ break;
+ }
+ token = &tokens[token->parent];
+ }
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ if (token->type != type) {
+ return JSMN_ERROR_INVAL;
+ }
+ parser->toksuper = -1;
+ token->end = parser->pos + 1;
+ break;
+ }
+ }
+ /* Error if unmatched closing bracket */
+ if (i == -1) {
+ return JSMN_ERROR_INVAL;
+ }
+ for (; i >= 0; i--) {
+ token = &tokens[i];
+ if (token->start != -1 && token->end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+#endif
+ break;
+ case '\"':
+ r = jsmn_parse_string(parser, js, len, tokens, num_tokens);
+ if (r < 0) {
+ return r;
+ }
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL) {
+ tokens[parser->toksuper].size++;
+ }
+ break;
+ case '\t':
+ case '\r':
+ case '\n':
+ case ' ':
+ break;
+ case ':':
+ parser->toksuper = parser->toknext - 1;
+ break;
+ case ',':
+ if (tokens != NULL && parser->toksuper != -1 &&
+ tokens[parser->toksuper].type != JSMN_ARRAY &&
+ tokens[parser->toksuper].type != JSMN_OBJECT) {
+#ifdef JSMN_PARENT_LINKS
+ parser->toksuper = tokens[parser->toksuper].parent;
+#else
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) {
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ parser->toksuper = i;
+ break;
+ }
+ }
+ }
+#endif
+ }
+ break;
+#ifdef JSMN_STRICT
+ /* In strict mode primitives are: numbers and booleans */
+ case '-':
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ case 't':
+ case 'f':
+ case 'n':
+ /* And they must not be keys of the object */
+ if (tokens != NULL && parser->toksuper != -1) {
+ const jsmntok_t *t = &tokens[parser->toksuper];
+ if (t->type == JSMN_OBJECT ||
+ (t->type == JSMN_STRING && t->size != 0)) {
+ return JSMN_ERROR_INVAL;
+ }
+ }
+#else
+ /* In non-strict mode every unquoted value is a primitive */
+ default:
+#endif
+ r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens);
+ if (r < 0) {
+ return r;
+ }
+ count++;
+ if (parser->toksuper != -1 && tokens != NULL) {
+ tokens[parser->toksuper].size++;
+ }
+ break;
+
+#ifdef JSMN_STRICT
+ /* Unexpected char in strict mode */
+ default:
+ return JSMN_ERROR_INVAL;
+#endif
+ }
+ }
+
+ if (tokens != NULL) {
+ for (i = parser->toknext - 1; i >= 0; i--) {
+ /* Unmatched opened object or array */
+ if (tokens[i].start != -1 && tokens[i].end == -1) {
+ return JSMN_ERROR_PART;
+ }
+ }
+ }
+
+ return count;
+}
+
+/**
+ * Creates a new parser based over a given buffer with an array of tokens
+ * available.
+ */
+JSMN_API void jsmn_init(jsmn_parser *parser) {
+ parser->pos = 0;
+ parser->toknext = 0;
+ parser->toksuper = -1;
+}
+
+/**
+ * Parse JSON string and fill tokens into self-allocated buffer.
+ */
+JSMN_API jsmntok_t *jsmn_parse_alloc(const char *js, const size_t len,
+ unsigned int *num_tokens)
+{
+
+ ssize_t token_count;
+ jsmn_parser parser;
+ jsmntok_t *tokens;
+ int ret;
+
+ jsmn_init(&parser);
+
+ /* Figure out how many tokens we need. */
+ ret = jsmn_parse(&parser, js, len, NULL, 0);
+ if (ret < 0)
+ return NULL;
+
+ token_count = ret;
+
+ tokens = kmalloc_array(token_count, sizeof(jsmntok_t), GFP_KERNEL);
+ if (!tokens)
+ return NULL;
+
+ jsmn_init(&parser);
+ ret = jsmn_parse(&parser, js, len, tokens, token_count);
+ if (ret < 0) {
+ free(tokens);
+ return NULL;
+ }
+
+ if (num_tokens)
+ *num_tokens = ret;
+ return tokens;
+}
+JSMN_API bool jsmn_eq(const char *val, const char *json, const jsmntok_t *token)
+{
+ size_t token_size = jsmn_token_size(token);
+ return strlen(val) == token_size
+ && strncmp(json + token->start, val, token_size) == 0;
+}
+
+JSMN_API bool jsmn_str_eq(const char *str, const char *json, const jsmntok_t *token)
+{
+ return token->type == JSMN_STRING && jsmn_eq(str, json, token);
+}
+
+static bool jsmn_case_eq(const char *val, const char *json, const jsmntok_t *token)
+{
+ size_t token_size = jsmn_token_size(token);
+ return strlen(val) == token_size
+ && strncasecmp(json + token->start, val, token_size) == 0;
+}
+
+JSMN_API bool jsmn_strcase_eq(const char *str, const char *json, const jsmntok_t *token)
+{
+ return token->type == JSMN_STRING && jsmn_case_eq(str, json, token);
+}
+
+JSMN_API const jsmntok_t *jsmn_skip_value(const jsmntok_t *tokens)
+{
+ int max_index = tokens[0].end;
+ do {
+ ++tokens;
+ } while (tokens[0].start < max_index);
+ return &tokens[0];
+}
+
+JSMN_API const jsmntok_t *jsmn_find_value(const char *key, const char *json,
+ const jsmntok_t *tokens)
+{
+ int items;
+ if (tokens[0].type != JSMN_OBJECT || tokens[0].size == 0)
+ return NULL;
+
+ items = tokens[0].size;
+ ++tokens;
+
+ do {
+ if (jsmn_str_eq(key, json, tokens))
+ return &tokens[1];
+ tokens = --items ? jsmn_skip_value(&tokens[1]) : NULL;
+ } while (tokens);
+
+ return NULL;
+}
+
+JSMN_API const jsmntok_t *jsmn_locate(const char *path[], const char *json,
+ const jsmntok_t *tokens)
+{
+ int i = 0;
+ while (path[i] != NULL) {
+ const jsmntok_t *value = jsmn_find_value(path[i], json, tokens);
+ if (!value)
+ return NULL;
+
+ switch (value->type) {
+ case JSMN_OBJECT:
+ case JSMN_ARRAY:
+ tokens = value;
+ ++i;
+ break;
+ case JSMN_UNDEFINED:
+ case JSMN_STRING:
+ case JSMN_PRIMITIVE:
+ return value;
+ }
+ }
+
+ return tokens;
+}
+
+JSMN_API char *jsmn_strdup(const char *path[], const char *json,
+ const jsmntok_t *tokens)
+{
+ const jsmntok_t *node;
+ int value_size;
+ char *value;
+
+ node = jsmn_locate(path, json, tokens);
+ if (!node || node->type != JSMN_STRING)
+ return NULL;
+
+ value_size = jsmn_token_size(node);
+ value = malloc(value_size + 1);
+ if (value) {
+ strncpy(value, json + node->start, value_size);
+ value[value_size] = '\0';
+ }
+
+ return value;
+}
diff --git a/lib/kasan/Kconfig b/lib/kasan/Kconfig
new file mode 100644
index 0000000000..e96638304c
--- /dev/null
+++ b/lib/kasan/Kconfig
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+config HAVE_ARCH_KASAN
+ bool
+
+config CC_HAS_KASAN_GENERIC
+ def_bool $(cc-option, -fsanitize=kernel-address)
+
+config KASAN
+ bool "KASAN: runtime memory debugger"
+ depends on (HAVE_ARCH_KASAN && CC_HAS_KASAN_GENERIC)
+ depends on MALLOC_TLSF
+ select CONSTRUCTORS
+ help
+ Enables KASAN (KernelAddressSANitizer) - runtime memory debugger,
+ designed to find out-of-bounds accesses and use-after-free bugs.
diff --git a/lib/kasan/Makefile b/lib/kasan/Makefile
new file mode 100644
index 0000000000..e3f4bb61f9
--- /dev/null
+++ b/lib/kasan/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += generic_report.o generic.o report.o common.o test_kasan.o
+KASAN_SANITIZE_generic_report.o := n
+KASAN_SANITIZE_generic.o := n
+KASAN_SANITIZE_report.o := n
+KASAN_SANITIZE_common.o := n
+
+CC_FLAGS_KASAN_RUNTIME := $(call cc-option, -fno-conserve-stack)
+CC_FLAGS_KASAN_RUNTIME += -fno-stack-protector
+
+CFLAGS_generic_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_generic.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_report.o := $(CC_FLAGS_KASAN_RUNTIME)
+CFLAGS_common.o := $(CC_FLAGS_KASAN_RUNTIME)
diff --git a/lib/kasan/common.c b/lib/kasan/common.c
new file mode 100644
index 0000000000..1ebf66a7b8
--- /dev/null
+++ b/lib/kasan/common.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains common generic and tag-based KASAN code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <linux/kasan.h>
+#include <linux/kernel.h>
+
+#include "kasan.h"
+
+int kasan_depth;
+
+void kasan_enable_current(void)
+{
+ kasan_depth++;
+}
+
+void kasan_disable_current(void)
+{
+ kasan_depth--;
+}
+
+#undef memset
+void *memset(void *addr, int c, size_t len)
+{
+ if (!check_memory_region((unsigned long)addr, len, true, _RET_IP_))
+ return NULL;
+
+ return __memset(addr, c, len);
+}
+
+#ifdef __HAVE_ARCH_MEMMOVE
+#undef memmove
+void *memmove(void *dest, const void *src, size_t len)
+{
+ if (!check_memory_region((unsigned long)src, len, false, _RET_IP_) ||
+ !check_memory_region((unsigned long)dest, len, true, _RET_IP_))
+ return NULL;
+
+ return __memmove(dest, src, len);
+}
+#endif
+
+#undef memcpy
+void *memcpy(void *dest, const void *src, size_t len)
+{
+ if (!check_memory_region((unsigned long)src, len, false, _RET_IP_) ||
+ !check_memory_region((unsigned long)dest, len, true, _RET_IP_))
+ return NULL;
+
+ return __memcpy(dest, src, len);
+}
+
+/*
+ * Poisons the shadow memory for 'size' bytes starting from 'addr'.
+ * Memory addresses should be aligned to KASAN_SHADOW_SCALE_SIZE.
+ */
+void kasan_poison_shadow(const void *address, size_t size, u8 value)
+{
+ void *shadow_start, *shadow_end;
+
+ /*
+ * Perform shadow offset calculation based on untagged address, as
+ * some of the callers (e.g. kasan_poison_object_data) pass tagged
+ * addresses to this function.
+ */
+ address = reset_tag(address);
+
+ shadow_start = kasan_mem_to_shadow(address);
+ shadow_end = kasan_mem_to_shadow(address + size);
+
+ __memset(shadow_start, value, shadow_end - shadow_start);
+}
+
+void kasan_unpoison_shadow(const void *address, size_t size)
+{
+ u8 tag = get_tag(address);
+
+ /*
+ * Perform shadow offset calculation based on untagged address, as
+ * some of the callers (e.g. kasan_unpoison_object_data) pass tagged
+ * addresses to this function.
+ */
+ address = reset_tag(address);
+
+ kasan_poison_shadow(address, size, tag);
+
+ if (size & KASAN_SHADOW_MASK) {
+ u8 *shadow = (u8 *)kasan_mem_to_shadow(address + size);
+
+ if (IS_ENABLED(CONFIG_KASAN_SW_TAGS))
+ *shadow = tag;
+ else
+ *shadow = size & KASAN_SHADOW_MASK;
+ }
+}
diff --git a/lib/kasan/generic.c b/lib/kasan/generic.c
new file mode 100644
index 0000000000..3709b8da9a
--- /dev/null
+++ b/lib/kasan/generic.c
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains core generic KASAN code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+
+#include "kasan.h"
+
+unsigned long kasan_shadow_start;
+unsigned long kasan_shadow_base;
+unsigned long kasan_shadowed_end;
+
+/*
+ * All functions below always inlined so compiler could
+ * perform better optimizations in each of __asan_loadX/__assn_storeX
+ * depending on memory access size X.
+ */
+
+static __always_inline bool memory_is_poisoned_1(unsigned long addr)
+{
+ s8 shadow_value = *(s8 *)kasan_mem_to_shadow((void *)addr);
+
+ if (unlikely(shadow_value)) {
+ s8 last_accessible_byte = addr & KASAN_SHADOW_MASK;
+ return unlikely(last_accessible_byte >= shadow_value);
+ }
+
+ return false;
+}
+
+static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr,
+ unsigned long size)
+{
+ u8 *shadow_addr = (u8 *)kasan_mem_to_shadow((void *)addr);
+
+ /*
+ * Access crosses 8(shadow size)-byte boundary. Such access maps
+ * into 2 shadow bytes, so we need to check them both.
+ */
+ if (unlikely(((addr + size - 1) & KASAN_SHADOW_MASK) < size - 1))
+ return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
+
+ return memory_is_poisoned_1(addr + size - 1);
+}
+
+static __always_inline bool memory_is_poisoned_16(unsigned long addr)
+{
+ u16 *shadow_addr = (u16 *)kasan_mem_to_shadow((void *)addr);
+
+ /* Unaligned 16-bytes access maps into 3 shadow bytes. */
+ if (unlikely(!IS_ALIGNED(addr, KASAN_SHADOW_SCALE_SIZE)))
+ return *shadow_addr || memory_is_poisoned_1(addr + 15);
+
+ return *shadow_addr;
+}
+
+static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
+ size_t size)
+{
+ while (size) {
+ if (unlikely(*start))
+ return (unsigned long)start;
+ start++;
+ size--;
+ }
+
+ return 0;
+}
+
+static __always_inline unsigned long memory_is_nonzero(const void *start,
+ const void *end)
+{
+ unsigned int words;
+ unsigned long ret;
+ unsigned int prefix = (unsigned long)start % 8;
+
+ if (end - start <= 16)
+ return bytes_is_nonzero(start, end - start);
+
+ if (prefix) {
+ prefix = 8 - prefix;
+ ret = bytes_is_nonzero(start, prefix);
+ if (unlikely(ret))
+ return ret;
+ start += prefix;
+ }
+
+ words = (end - start) / 8;
+ while (words) {
+ if (unlikely(*(u64 *)start))
+ return bytes_is_nonzero(start, 8);
+ start += 8;
+ words--;
+ }
+
+ return bytes_is_nonzero(start, (end - start) % 8);
+}
+
+static __always_inline bool memory_is_poisoned_n(unsigned long addr,
+ size_t size)
+{
+ unsigned long ret;
+
+ ret = memory_is_nonzero(kasan_mem_to_shadow((void *)addr),
+ kasan_mem_to_shadow((void *)addr + size - 1) + 1);
+
+ if (unlikely(ret)) {
+ unsigned long last_byte = addr + size - 1;
+ s8 *last_shadow = (s8 *)kasan_mem_to_shadow((void *)last_byte);
+
+ if (unlikely(ret != (unsigned long)last_shadow ||
+ ((long)(last_byte & KASAN_SHADOW_MASK) >= *last_shadow)))
+ return true;
+ }
+ return false;
+}
+
+static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
+{
+ if (__builtin_constant_p(size)) {
+ switch (size) {
+ case 1:
+ return memory_is_poisoned_1(addr);
+ case 2:
+ case 4:
+ case 8:
+ return memory_is_poisoned_2_4_8(addr, size);
+ case 16:
+ return memory_is_poisoned_16(addr);
+ default:
+ BUILD_BUG();
+ }
+ }
+
+ return memory_is_poisoned_n(addr, size);
+}
+
+static bool kasan_initialized;
+
+static __always_inline bool check_memory_region_inline(unsigned long addr,
+ size_t size, bool write,
+ unsigned long ret_ip)
+{
+ if (!kasan_initialized)
+ return true;
+
+ if (addr < kasan_shadow_start)
+ return true;
+
+ if (addr > kasan_shadowed_end)
+ return true;
+
+ if (unlikely(size == 0))
+ return true;
+
+ if (unlikely(addr + size < addr))
+ return !kasan_report(addr, size, write, ret_ip);
+
+ if (addr < kasan_shadow_base)
+ return true;
+
+ if (likely(!memory_is_poisoned(addr, size)))
+ return true;
+
+ return !kasan_report(addr, size, write, ret_ip);
+}
+
+void kasan_init(unsigned long membase, unsigned long memsize,
+ unsigned long shadow_base)
+{
+ kasan_shadow_start = membase;
+ kasan_shadow_base = shadow_base;
+ kasan_shadowed_end = membase + memsize - 1;
+
+ kasan_unpoison_shadow((void *)membase, memsize);
+ kasan_initialized = true;
+}
+
+bool __no_sanitize_address check_memory_region(unsigned long addr,
+ size_t size, bool write,
+ unsigned long ret_ip)
+{
+ return check_memory_region_inline(addr, size, write, ret_ip);
+}
+
+static void register_global(struct kasan_global *global)
+{
+ size_t aligned_size = round_up(global->size, KASAN_SHADOW_SCALE_SIZE);
+
+ kasan_unpoison_shadow(global->beg, global->size);
+
+ kasan_poison_shadow(global->beg + aligned_size,
+ global->size_with_redzone - aligned_size,
+ KASAN_GLOBAL_REDZONE);
+}
+
+void __asan_register_globals(struct kasan_global *globals, size_t size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ register_global(&globals[i]);
+}
+EXPORT_SYMBOL(__asan_register_globals);
+
+void __asan_unregister_globals(struct kasan_global *globals, size_t size)
+{
+}
+EXPORT_SYMBOL(__asan_unregister_globals);
+
+#define DEFINE_ASAN_LOAD_STORE(size) \
+ void __no_sanitize_address __asan_load##size(unsigned long addr) \
+ { \
+ check_memory_region_inline(addr, size, false, _RET_IP_);\
+ } \
+ EXPORT_SYMBOL(__asan_load##size); \
+ __alias(__asan_load##size) \
+ void __no_sanitize_address __asan_load##size##_noabort(unsigned long); \
+ EXPORT_SYMBOL(__asan_load##size##_noabort); \
+ void __asan_store##size(unsigned long addr) \
+ { \
+ check_memory_region_inline(addr, size, true, _RET_IP_); \
+ } \
+ EXPORT_SYMBOL(__asan_store##size); \
+ __alias(__asan_store##size) \
+ void __asan_store##size##_noabort(unsigned long); \
+ EXPORT_SYMBOL(__asan_store##size##_noabort)
+
+DEFINE_ASAN_LOAD_STORE(1);
+DEFINE_ASAN_LOAD_STORE(2);
+DEFINE_ASAN_LOAD_STORE(4);
+DEFINE_ASAN_LOAD_STORE(8);
+DEFINE_ASAN_LOAD_STORE(16);
+
+void __asan_loadN(unsigned long addr, size_t size)
+{
+ check_memory_region(addr, size, false, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_loadN);
+
+__alias(__asan_loadN)
+void __asan_loadN_noabort(unsigned long, size_t);
+EXPORT_SYMBOL(__asan_loadN_noabort);
+
+void __asan_storeN(unsigned long addr, size_t size)
+{
+ check_memory_region(addr, size, true, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_storeN);
+
+__alias(__asan_storeN)
+void __asan_storeN_noabort(unsigned long, size_t);
+EXPORT_SYMBOL(__asan_storeN_noabort);
+
+/* to shut up compiler complaints */
+void __asan_handle_no_return(void) {}
+EXPORT_SYMBOL(__asan_handle_no_return);
+
+/* Emitted by compiler to poison alloca()ed objects. */
+void __asan_alloca_poison(unsigned long addr, size_t size)
+{
+ size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE);
+ size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) -
+ rounded_up_size;
+ size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE);
+
+ const void *left_redzone = (const void *)(addr -
+ KASAN_ALLOCA_REDZONE_SIZE);
+ const void *right_redzone = (const void *)(addr + rounded_up_size);
+
+ WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE));
+
+ kasan_unpoison_shadow((const void *)(addr + rounded_down_size),
+ size - rounded_down_size);
+ kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_LEFT);
+ kasan_poison_shadow(right_redzone,
+ padding_size + KASAN_ALLOCA_REDZONE_SIZE,
+ KASAN_ALLOCA_RIGHT);
+}
+EXPORT_SYMBOL(__asan_alloca_poison);
+
+/* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom)
+{
+ if (unlikely(!stack_top || stack_top > stack_bottom))
+ return;
+
+ kasan_unpoison_shadow(stack_top, stack_bottom - stack_top);
+}
+EXPORT_SYMBOL(__asan_allocas_unpoison);
+
+/* Emitted by the compiler to [un]poison local variables. */
+#define DEFINE_ASAN_SET_SHADOW(byte) \
+ void __asan_set_shadow_##byte(const void *addr, size_t size) \
+ { \
+ __memset((void *)addr, 0x##byte, size); \
+ } \
+ EXPORT_SYMBOL(__asan_set_shadow_##byte)
+
+DEFINE_ASAN_SET_SHADOW(00);
+DEFINE_ASAN_SET_SHADOW(f1);
+DEFINE_ASAN_SET_SHADOW(f2);
+DEFINE_ASAN_SET_SHADOW(f3);
+DEFINE_ASAN_SET_SHADOW(f5);
+DEFINE_ASAN_SET_SHADOW(f8);
diff --git a/lib/kasan/generic_report.c b/lib/kasan/generic_report.c
new file mode 100644
index 0000000000..1cc5829e8d
--- /dev/null
+++ b/lib/kasan/generic_report.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains generic KASAN specific error reporting code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+
+#include <asm/sections.h>
+
+#include "kasan.h"
+
+void *find_first_bad_addr(void *addr, size_t size)
+{
+ void *p = addr;
+
+ while (p < addr + size && !(*(u8 *)kasan_mem_to_shadow(p)))
+ p += KASAN_SHADOW_SCALE_SIZE;
+ return p;
+}
+
+static const char *get_shadow_bug_type(struct kasan_access_info *info)
+{
+ const char *bug_type = "unknown-crash";
+ u8 *shadow_addr;
+
+ shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr);
+
+ /*
+ * If shadow byte value is in [0, KASAN_SHADOW_SCALE_SIZE) we can look
+ * at the next shadow byte to determine the type of the bad access.
+ */
+ if (*shadow_addr > 0 && *shadow_addr <= KASAN_SHADOW_SCALE_SIZE - 1)
+ shadow_addr++;
+
+ switch (*shadow_addr) {
+ case 0 ... KASAN_SHADOW_SCALE_SIZE - 1:
+ /*
+ * In theory it's still possible to see these shadow values
+ * due to a data race in the kernel code.
+ */
+ bug_type = "out-of-bounds";
+ break;
+ case KASAN_PAGE_REDZONE:
+ case KASAN_KMALLOC_REDZONE:
+ bug_type = "slab-out-of-bounds";
+ break;
+ case KASAN_GLOBAL_REDZONE:
+ bug_type = "global-out-of-bounds";
+ break;
+ case KASAN_STACK_LEFT:
+ case KASAN_STACK_MID:
+ case KASAN_STACK_RIGHT:
+ case KASAN_STACK_PARTIAL:
+ bug_type = "stack-out-of-bounds";
+ break;
+ case KASAN_FREE_PAGE:
+ case KASAN_KMALLOC_FREE:
+ case KASAN_KMALLOC_FREETRACK:
+ bug_type = "use-after-free";
+ break;
+ case KASAN_ALLOCA_LEFT:
+ case KASAN_ALLOCA_RIGHT:
+ bug_type = "alloca-out-of-bounds";
+ break;
+ case KASAN_VMALLOC_INVALID:
+ bug_type = "vmalloc-out-of-bounds";
+ break;
+ }
+
+ return bug_type;
+}
+
+static const char *get_wild_bug_type(struct kasan_access_info *info)
+{
+ const char *bug_type = "unknown-crash";
+
+ if ((unsigned long)info->access_addr < PAGE_SIZE)
+ bug_type = "null-ptr-deref";
+ else
+ bug_type = "wild-memory-access";
+
+ return bug_type;
+}
+
+const char *get_bug_type(struct kasan_access_info *info)
+{
+ /*
+ * If access_size is a negative number, then it has reason to be
+ * defined as out-of-bounds bug type.
+ *
+ * Casting negative numbers to size_t would indeed turn up as
+ * a large size_t and its value will be larger than ULONG_MAX/2,
+ * so that this can qualify as out-of-bounds.
+ */
+ if (info->access_addr + info->access_size < info->access_addr)
+ return "out-of-bounds";
+
+ if (addr_has_shadow(info->access_addr))
+ return get_shadow_bug_type(info);
+ return get_wild_bug_type(info);
+}
+
+#define DEFINE_ASAN_REPORT_LOAD(size) \
+void __asan_report_load##size##_noabort(unsigned long addr) \
+{ \
+ kasan_report(addr, size, false, _RET_IP_); \
+} \
+EXPORT_SYMBOL(__asan_report_load##size##_noabort)
+
+#define DEFINE_ASAN_REPORT_STORE(size) \
+void __asan_report_store##size##_noabort(unsigned long addr) \
+{ \
+ kasan_report(addr, size, true, _RET_IP_); \
+} \
+EXPORT_SYMBOL(__asan_report_store##size##_noabort)
+
+DEFINE_ASAN_REPORT_LOAD(1);
+DEFINE_ASAN_REPORT_LOAD(2);
+DEFINE_ASAN_REPORT_LOAD(4);
+DEFINE_ASAN_REPORT_LOAD(8);
+DEFINE_ASAN_REPORT_LOAD(16);
+DEFINE_ASAN_REPORT_STORE(1);
+DEFINE_ASAN_REPORT_STORE(2);
+DEFINE_ASAN_REPORT_STORE(4);
+DEFINE_ASAN_REPORT_STORE(8);
+DEFINE_ASAN_REPORT_STORE(16);
+
+void __asan_report_load_n_noabort(unsigned long addr, size_t size)
+{
+ kasan_report(addr, size, false, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_report_load_n_noabort);
+
+void __asan_report_store_n_noabort(unsigned long addr, size_t size)
+{
+ kasan_report(addr, size, true, _RET_IP_);
+}
+EXPORT_SYMBOL(__asan_report_store_n_noabort);
diff --git a/lib/kasan/kasan.h b/lib/kasan/kasan.h
new file mode 100644
index 0000000000..e17f49dbec
--- /dev/null
+++ b/lib/kasan/kasan.h
@@ -0,0 +1,164 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __MM_KASAN_KASAN_H
+#define __MM_KASAN_KASAN_H
+
+#include <linux/kasan.h>
+#include <linux/linkage.h>
+
+#define KASAN_SHADOW_SCALE_SIZE (1UL << KASAN_SHADOW_SCALE_SHIFT)
+#define KASAN_SHADOW_MASK (KASAN_SHADOW_SCALE_SIZE - 1)
+
+#define KASAN_ALLOCA_REDZONE_SIZE 32
+
+/*
+ * Stack frame marker (compiler ABI).
+ */
+#define KASAN_CURRENT_STACK_FRAME_MAGIC 0x41B58AB3
+
+/* Don't break randconfig/all*config builds */
+#ifndef KASAN_ABI_VERSION
+#define KASAN_ABI_VERSION 1
+#endif
+
+struct kasan_access_info {
+ const void *access_addr;
+ const void *first_bad_addr;
+ size_t access_size;
+ bool is_write;
+ unsigned long ip;
+};
+
+/* The layout of struct dictated by compiler */
+struct kasan_source_location {
+ const char *filename;
+ int line_no;
+ int column_no;
+};
+
+/* The layout of struct dictated by compiler */
+struct kasan_global {
+ const void *beg; /* Address of the beginning of the global variable. */
+ size_t size; /* Size of the global variable. */
+ size_t size_with_redzone; /* Size of the variable + size of the red zone. 32 bytes aligned */
+ const void *name;
+ const void *module_name; /* Name of the module where the global variable is declared. */
+ unsigned long has_dynamic_init; /* This needed for C++ */
+#if KASAN_ABI_VERSION >= 4
+ struct kasan_source_location *location;
+#endif
+#if KASAN_ABI_VERSION >= 5
+ char *odr_indicator;
+#endif
+};
+
+static inline const void *kasan_shadow_to_mem(const void *shadow_addr)
+{
+ unsigned long sa = (unsigned long)shadow_addr;
+
+ sa -= kasan_shadow_base;
+ sa <<= KASAN_SHADOW_SCALE_SHIFT;
+ sa += kasan_shadow_start;
+
+ return (void *)sa;
+}
+
+static inline bool addr_has_shadow(const void *addr)
+{
+ return (addr >= (void *)kasan_shadow_start);
+}
+
+/**
+ * check_memory_region - Check memory region, and report if invalid access.
+ * @addr: the accessed address
+ * @size: the accessed size
+ * @write: true if access is a write access
+ * @ret_ip: return address
+ * @return: true if access was valid, false if invalid
+ */
+bool check_memory_region(unsigned long addr, size_t size, bool write,
+ unsigned long ret_ip);
+
+void *find_first_bad_addr(void *addr, size_t size);
+const char *get_bug_type(struct kasan_access_info *info);
+
+bool kasan_report(unsigned long addr, size_t size,
+ bool is_write, unsigned long ip);
+void kasan_report_invalid_free(void *object, unsigned long ip);
+
+#ifndef arch_kasan_set_tag
+static inline const void *arch_kasan_set_tag(const void *addr, u8 tag)
+{
+ return addr;
+}
+#endif
+#ifndef arch_kasan_reset_tag
+#define arch_kasan_reset_tag(addr) ((void *)(addr))
+#endif
+#ifndef arch_kasan_get_tag
+#define arch_kasan_get_tag(addr) 0
+#endif
+
+#define set_tag(addr, tag) ((void *)arch_kasan_set_tag((addr), (tag)))
+#define reset_tag(addr) ((void *)arch_kasan_reset_tag(addr))
+#define get_tag(addr) arch_kasan_get_tag(addr)
+
+/*
+ * Exported functions for interfaces called from assembly or from generated
+ * code. Declarations here to avoid warning about missing declarations.
+ */
+asmlinkage void kasan_unpoison_task_stack_below(const void *watermark);
+void __asan_register_globals(struct kasan_global *globals, size_t size);
+void __asan_unregister_globals(struct kasan_global *globals, size_t size);
+void __asan_handle_no_return(void);
+void __asan_alloca_poison(unsigned long addr, size_t size);
+void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom);
+
+void __asan_load1(unsigned long addr);
+void __asan_store1(unsigned long addr);
+void __asan_load2(unsigned long addr);
+void __asan_store2(unsigned long addr);
+void __asan_load4(unsigned long addr);
+void __asan_store4(unsigned long addr);
+void __asan_load8(unsigned long addr);
+void __asan_store8(unsigned long addr);
+void __asan_load16(unsigned long addr);
+void __asan_store16(unsigned long addr);
+void __asan_loadN(unsigned long addr, size_t size);
+void __asan_storeN(unsigned long addr, size_t size);
+
+void __asan_load1_noabort(unsigned long addr);
+void __asan_store1_noabort(unsigned long addr);
+void __asan_load2_noabort(unsigned long addr);
+void __asan_store2_noabort(unsigned long addr);
+void __asan_load4_noabort(unsigned long addr);
+void __asan_store4_noabort(unsigned long addr);
+void __asan_load8_noabort(unsigned long addr);
+void __asan_store8_noabort(unsigned long addr);
+void __asan_load16_noabort(unsigned long addr);
+void __asan_store16_noabort(unsigned long addr);
+void __asan_loadN_noabort(unsigned long addr, size_t size);
+void __asan_storeN_noabort(unsigned long addr, size_t size);
+
+void __asan_report_load1_noabort(unsigned long addr);
+void __asan_report_store1_noabort(unsigned long addr);
+void __asan_report_load2_noabort(unsigned long addr);
+void __asan_report_store2_noabort(unsigned long addr);
+void __asan_report_load4_noabort(unsigned long addr);
+void __asan_report_store4_noabort(unsigned long addr);
+void __asan_report_load8_noabort(unsigned long addr);
+void __asan_report_store8_noabort(unsigned long addr);
+void __asan_report_load16_noabort(unsigned long addr);
+void __asan_report_store16_noabort(unsigned long addr);
+void __asan_report_load_n_noabort(unsigned long addr, size_t size);
+void __asan_report_store_n_noabort(unsigned long addr, size_t size);
+
+void __asan_set_shadow_00(const void *addr, size_t size);
+void __asan_set_shadow_f1(const void *addr, size_t size);
+void __asan_set_shadow_f2(const void *addr, size_t size);
+void __asan_set_shadow_f3(const void *addr, size_t size);
+void __asan_set_shadow_f5(const void *addr, size_t size);
+void __asan_set_shadow_f8(const void *addr, size_t size);
+
+extern int kasan_depth;
+
+#endif
diff --git a/lib/kasan/report.c b/lib/kasan/report.c
new file mode 100644
index 0000000000..a9050546e7
--- /dev/null
+++ b/lib/kasan/report.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file contains common generic and tag-based KASAN error reporting code.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com>
+ *
+ * Some code borrowed from https://github.com/xairy/kasan-prototype by
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/printk.h>
+#include <asm-generic/sections.h>
+
+#include "kasan.h"
+
+/* Shadow layout customization. */
+#define SHADOW_BYTES_PER_BLOCK 1
+#define SHADOW_BLOCKS_PER_ROW 16
+#define SHADOW_BYTES_PER_ROW (SHADOW_BLOCKS_PER_ROW * SHADOW_BYTES_PER_BLOCK)
+#define SHADOW_ROWS_AROUND_ADDR 2
+
+static unsigned long kasan_flags;
+
+#define KASAN_BIT_REPORTED 0
+#define KASAN_BIT_MULTI_SHOT 1
+
+bool kasan_save_enable_multi_shot(void)
+{
+ return test_and_set_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
+}
+EXPORT_SYMBOL_GPL(kasan_save_enable_multi_shot);
+
+void kasan_restore_multi_shot(bool enabled)
+{
+ if (!enabled)
+ clear_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags);
+}
+EXPORT_SYMBOL_GPL(kasan_restore_multi_shot);
+
+static void print_error_description(struct kasan_access_info *info)
+{
+ eprintf("BUG: KASAN: %s in %pS\n",
+ get_bug_type(info), (void *)info->ip);
+ eprintf("%s of size %zu at addr %px\n",
+ info->is_write ? "Write" : "Read", info->access_size,
+ info->access_addr);
+}
+
+static void start_report(unsigned long *flags)
+{
+ /*
+ * Make sure we don't end up in loop.
+ */
+ kasan_disable_current();
+ eprintf("==================================================================\n");
+}
+
+static void end_report(unsigned long *flags)
+{
+ eprintf("==================================================================\n");
+ kasan_enable_current();
+}
+
+static inline bool kernel_or_module_addr(const void *addr)
+{
+ if (addr >= (void *)_stext && addr < (void *)_end)
+ return true;
+ return false;
+}
+
+static void print_address_description(void *addr, u8 tag)
+{
+ dump_stack();
+ eprintf("\n");
+
+ if (kernel_or_module_addr(addr)) {
+ eprintf("The buggy address belongs to the variable:\n");
+ eprintf(" %pS\n", addr);
+ }
+}
+
+static bool row_is_guilty(const void *row, const void *guilty)
+{
+ return (row <= guilty) && (guilty < row + SHADOW_BYTES_PER_ROW);
+}
+
+static int shadow_pointer_offset(const void *row, const void *shadow)
+{
+ /* The length of ">ff00ff00ff00ff00: " is
+ * 3 + (BITS_PER_LONG/8)*2 chars.
+ */
+ return 3 + (BITS_PER_LONG/8)*2 + (shadow - row)*2 +
+ (shadow - row) / SHADOW_BYTES_PER_BLOCK + 1;
+}
+
+static void print_shadow_for_address(const void *addr)
+{
+ int i;
+ const void *shadow = kasan_mem_to_shadow(addr);
+ const void *shadow_row;
+
+ shadow_row = (void *)round_down((unsigned long)shadow,
+ SHADOW_BYTES_PER_ROW)
+ - SHADOW_ROWS_AROUND_ADDR * SHADOW_BYTES_PER_ROW;
+
+ eprintf("Memory state around the buggy address:\n");
+
+ for (i = -SHADOW_ROWS_AROUND_ADDR; i <= SHADOW_ROWS_AROUND_ADDR; i++) {
+ const void *kaddr = kasan_shadow_to_mem(shadow_row);
+ char buffer[4 + (BITS_PER_LONG/8)*2];
+ char shadow_buf[SHADOW_BYTES_PER_ROW];
+
+ snprintf(buffer, sizeof(buffer),
+ (i == 0) ? ">%px: " : " %px: ", kaddr);
+ /*
+ * We should not pass a shadow pointer to generic
+ * function, because generic functions may try to
+ * access kasan mapping for the passed address.
+ */
+ memcpy(shadow_buf, shadow_row, SHADOW_BYTES_PER_ROW);
+ print_hex_dump(KERN_ERR, buffer,
+ DUMP_PREFIX_NONE, SHADOW_BYTES_PER_ROW, 1,
+ shadow_buf, SHADOW_BYTES_PER_ROW, 0);
+
+ if (row_is_guilty(shadow_row, shadow))
+ eprintf("%*c\n",
+ shadow_pointer_offset(shadow_row, shadow),
+ '^');
+
+ shadow_row += SHADOW_BYTES_PER_ROW;
+ }
+}
+
+static bool report_enabled(void)
+{
+ if (kasan_depth)
+ return false;
+ if (test_bit(KASAN_BIT_MULTI_SHOT, &kasan_flags))
+ return true;
+ return !test_and_set_bit(KASAN_BIT_REPORTED, &kasan_flags);
+}
+
+static void __kasan_report(unsigned long addr, size_t size, bool is_write,
+ unsigned long ip)
+{
+ struct kasan_access_info info;
+ void *tagged_addr;
+ void *untagged_addr;
+ unsigned long flags;
+
+ tagged_addr = (void *)addr;
+ untagged_addr = reset_tag(tagged_addr);
+
+ info.access_addr = tagged_addr;
+ if (addr_has_shadow(untagged_addr))
+ info.first_bad_addr = find_first_bad_addr(tagged_addr, size);
+ else
+ info.first_bad_addr = untagged_addr;
+ info.access_size = size;
+ info.is_write = is_write;
+ info.ip = ip;
+
+ start_report(&flags);
+
+ print_error_description(&info);
+ eprintf("\n");
+
+ if (addr_has_shadow(untagged_addr)) {
+ print_address_description(untagged_addr, get_tag(tagged_addr));
+ eprintf("\n");
+ print_shadow_for_address(info.first_bad_addr);
+ } else {
+ dump_stack();
+ }
+
+ end_report(&flags);
+}
+
+bool kasan_report(unsigned long addr, size_t size, bool is_write,
+ unsigned long ip)
+{
+ bool ret = false;
+
+ if (likely(report_enabled())) {
+ __kasan_report(addr, size, is_write, ip);
+ ret = true;
+ }
+
+ return ret;
+}
diff --git a/lib/kasan/test_kasan.c b/lib/kasan/test_kasan.c
new file mode 100644
index 0000000000..a74251a6d9
--- /dev/null
+++ b/lib/kasan/test_kasan.c
@@ -0,0 +1,492 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Author: Andrey Ryabinin <a.ryabinin@samsung.com>
+ */
+
+#define pr_fmt(fmt) "kasan test: " fmt
+
+#include <common.h>
+#include <command.h>
+#include <complete.h>
+
+#include "kasan.h"
+
+/*
+ * We assign some test results to these globals to make sure the tests
+ * are not eliminated as dead code.
+ */
+
+int kasan_int_result;
+void *kasan_ptr_result;
+
+/*
+ * Note: test functions are marked noinline so that their names appear in
+ * reports.
+ */
+
+static noinline void malloc_oob_right(void)
+{
+ char *ptr;
+ size_t size = 123;
+
+ pr_info("out-of-bounds to right\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ OPTIMIZER_HIDE_VAR(ptr);
+
+ ptr[size] = 'x';
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_left(void)
+{
+ char *ptr;
+ size_t size = 15;
+
+ pr_info("out-of-bounds to left\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ OPTIMIZER_HIDE_VAR(ptr);
+
+ *ptr = *(ptr - 1);
+ free(ptr);
+}
+
+static noinline void malloc_oob_realloc_more(void)
+{
+ char *ptr1, *ptr2;
+ size_t size1 = 17;
+ size_t size2 = 19;
+
+ pr_info("out-of-bounds after krealloc more\n");
+ ptr1 = malloc(size1);
+ ptr2 = realloc(ptr1, size2);
+ if (!ptr1 || !ptr2) {
+ pr_err("Allocation failed\n");
+ free(ptr1);
+ free(ptr2);
+ return;
+ }
+
+ OPTIMIZER_HIDE_VAR(ptr2);
+
+ ptr2[size2] = 'x';
+
+ free(ptr2);
+}
+
+static noinline void malloc_oob_realloc_less(void)
+{
+ char *ptr1, *ptr2;
+ size_t size1 = 17;
+ size_t size2 = 15;
+
+ pr_info("out-of-bounds after krealloc less\n");
+ ptr1 = malloc(size1);
+ ptr2 = realloc(ptr1, size2);
+ if (!ptr1 || !ptr2) {
+ pr_err("Allocation failed\n");
+ free(ptr1);
+ return;
+ }
+
+ OPTIMIZER_HIDE_VAR(ptr2);
+
+ ptr2[size2] = 'x';
+
+ free(ptr2);
+}
+
+static noinline void malloc_oob_16(void)
+{
+ struct {
+ u64 words[2];
+ } *ptr1, *ptr2;
+
+ pr_info("malloc out-of-bounds for 16-bytes access\n");
+ ptr1 = malloc(sizeof(*ptr1) - 3);
+ ptr2 = malloc(sizeof(*ptr2));
+ if (!ptr1 || !ptr2) {
+ pr_err("Allocation failed\n");
+ free(ptr1);
+ free(ptr2);
+ return;
+ }
+
+ OPTIMIZER_HIDE_VAR(ptr1);
+
+ *ptr1 = *ptr2;
+ free(ptr1);
+ free(ptr2);
+}
+
+static noinline void malloc_oob_memset_2(void)
+{
+ char *ptr;
+ size_t size = 8;
+
+ pr_info("out-of-bounds in memset2\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 7, 0, 2);
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_memset_4(void)
+{
+ char *ptr;
+ size_t size = 8;
+
+ pr_info("out-of-bounds in memset4\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 5, 0, 4);
+
+ free(ptr);
+}
+
+
+static noinline void malloc_oob_memset_8(void)
+{
+ char *ptr;
+ size_t size = 8;
+
+ pr_info("out-of-bounds in memset8\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 1, 0, 8);
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_memset_16(void)
+{
+ char *ptr;
+ size_t size = 16;
+
+ pr_info("out-of-bounds in memset16\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr + 1, 0, 16);
+
+ free(ptr);
+}
+
+static noinline void malloc_oob_in_memset(void)
+{
+ char *ptr;
+ size_t size = 666;
+
+ pr_info("out-of-bounds in memset\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ memset(ptr, 0, size + 5);
+
+ free(ptr);
+}
+
+static noinline void malloc_uaf(void)
+{
+ char *ptr;
+ size_t size = 10;
+
+ pr_info("use-after-free\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ free(ptr);
+ *(ptr + 8) = 'x';
+}
+
+static noinline void malloc_uaf_memset(void)
+{
+ char *ptr;
+ size_t size = 33;
+
+ pr_info("use-after-free in memset\n");
+ ptr = malloc(size);
+ if (!ptr) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ free(ptr);
+ memset(ptr, 0, size);
+}
+
+static noinline void malloc_uaf2(void)
+{
+ char *ptr1, *ptr2;
+ size_t size = 43;
+
+ pr_info("use-after-free after another malloc\n");
+ ptr1 = malloc(size);
+ if (!ptr1) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ free(ptr1);
+ ptr2 = malloc(size);
+ if (!ptr2) {
+ pr_err("Allocation failed\n");
+ return;
+ }
+
+ ptr1[40] = 'x';
+ if (ptr1 == ptr2)
+ pr_err("Could not detect use-after-free: ptr1 == ptr2\n");
+ free(ptr2);
+}
+
+static char global_array[10];
+
+static noinline void kasan_global_oob(void)
+{
+ volatile int i = 3;
+ char *p = &global_array[ARRAY_SIZE(global_array) + i];
+
+ pr_info("out-of-bounds global variable\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_stack_oob(void)
+{
+ char stack_array[10];
+ volatile int i = 0;
+ char *p = &stack_array[ARRAY_SIZE(stack_array) + i];
+
+ pr_info("out-of-bounds on stack\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_alloca_oob_left(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array - 1;
+
+ OPTIMIZER_HIDE_VAR(p);
+
+ pr_info("out-of-bounds to left on alloca\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_alloca_oob_right(void)
+{
+ volatile int i = 10;
+ char alloca_array[i];
+ char *p = alloca_array + i;
+
+ pr_info("out-of-bounds to right on alloca\n");
+ *(volatile char *)p;
+}
+
+static noinline void kasan_memchr(void)
+{
+ char *ptr;
+ size_t size = 24;
+
+ pr_info("out-of-bounds in memchr\n");
+ ptr = kzalloc(size, 0);
+ if (!ptr)
+ return;
+
+ kasan_ptr_result = memchr(ptr, '1', size + 1);
+ free(ptr);
+}
+
+static noinline void kasan_memcmp(void)
+{
+ char *ptr;
+ size_t size = 24;
+ int arr[9];
+
+ pr_info("out-of-bounds in memcmp\n");
+ ptr = kzalloc(size, 0);
+ if (!ptr)
+ return;
+
+ memset(arr, 0, sizeof(arr));
+ kasan_int_result = memcmp(ptr, arr, size + 1);
+ free(ptr);
+}
+
+static noinline void kasan_strings(void)
+{
+ char *ptr;
+ size_t size = 24;
+
+ pr_info("use-after-free in strchr\n");
+ ptr = malloc(size);
+ if (!ptr)
+ return;
+
+ free(ptr);
+
+ /*
+ * Try to cause only 1 invalid access (less spam in dmesg).
+ * For that we need ptr to point to zeroed byte.
+ * Skip metadata that could be stored in freed object so ptr
+ * will likely point to zeroed byte.
+ */
+ ptr += 16;
+ kasan_ptr_result = strchr(ptr, '1');
+
+ pr_info("use-after-free in strrchr\n");
+ kasan_ptr_result = strrchr(ptr, '1');
+
+ pr_info("use-after-free in strcmp\n");
+ kasan_int_result = strcmp(ptr, "2");
+
+ pr_info("use-after-free in strncmp\n");
+ kasan_int_result = strncmp(ptr, "2", 1);
+
+ pr_info("use-after-free in strlen\n");
+ kasan_int_result = strlen(ptr);
+
+ pr_info("use-after-free in strnlen\n");
+ kasan_int_result = strnlen(ptr, 1);
+}
+
+static noinline void kasan_bitops(void)
+{
+ /*
+ * Allocate 1 more byte, which causes kzalloc to round up to 16-bytes;
+ * this way we do not actually corrupt other memory.
+ */
+ long *bits = xzalloc(sizeof(*bits) + 1);
+ if (!bits)
+ return;
+
+ /*
+ * Below calls try to access bit within allocated memory; however, the
+ * below accesses are still out-of-bounds, since bitops are defined to
+ * operate on the whole long the bit is in.
+ */
+ pr_info("out-of-bounds in set_bit\n");
+ set_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in __set_bit\n");
+ __set_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in clear_bit\n");
+ clear_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in __clear_bit\n");
+ __clear_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in change_bit\n");
+ change_bit(BITS_PER_LONG, bits);
+
+ pr_info("out-of-bounds in __change_bit\n");
+ __change_bit(BITS_PER_LONG, bits);
+
+ /*
+ * Below calls try to access bit beyond allocated memory.
+ */
+ pr_info("out-of-bounds in test_and_set_bit\n");
+ test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in __test_and_set_bit\n");
+ __test_and_set_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in test_and_clear_bit\n");
+ test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in __test_and_clear_bit\n");
+ __test_and_clear_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in test_and_change_bit\n");
+ test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in __test_and_change_bit\n");
+ __test_and_change_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+ pr_info("out-of-bounds in test_bit\n");
+ kasan_int_result = test_bit(BITS_PER_LONG + BITS_PER_BYTE, bits);
+
+#if defined(clear_bit_unlock_is_negative_byte)
+ pr_info("out-of-bounds in clear_bit_unlock_is_negative_byte\n");
+ kasan_int_result = clear_bit_unlock_is_negative_byte(BITS_PER_LONG +
+ BITS_PER_BYTE, bits);
+#endif
+ free(bits);
+}
+
+static int do_kasan_test(int argc, char *argv[])
+{
+ /*
+ * Temporarily enable multi-shot mode. Otherwise, we'd only get a
+ * report for the first case.
+ */
+ bool multishot = kasan_save_enable_multi_shot();
+
+ malloc_oob_right();
+ malloc_oob_left();
+ malloc_oob_realloc_more();
+ malloc_oob_realloc_less();
+ malloc_oob_16();
+ malloc_oob_in_memset();
+ malloc_oob_memset_2();
+ malloc_oob_memset_4();
+ malloc_oob_memset_8();
+ malloc_oob_memset_16();
+ malloc_uaf();
+ malloc_uaf_memset();
+ malloc_uaf2();
+ kasan_stack_oob();
+ kasan_global_oob();
+ kasan_alloca_oob_left();
+ kasan_alloca_oob_right();
+ kasan_memchr();
+ kasan_memcmp();
+ kasan_strings();
+ kasan_bitops();
+
+ kasan_restore_multi_shot(multishot);
+
+ return 0;
+}
+
+BAREBOX_CMD_START(kasan_tests)
+ .cmd = do_kasan_test,
+ BAREBOX_CMD_DESC("Run KAsan tests")
+ BAREBOX_CMD_GROUP(CMD_GRP_MISC)
+ BAREBOX_CMD_COMPLETE(empty_complete)
+BAREBOX_CMD_END
diff --git a/lib/libbb.c b/lib/libbb.c
index d0c9bf4d80..642e54d78f 100644
--- a/lib/libbb.c
+++ b/lib/libbb.c
@@ -49,11 +49,47 @@ char *concat_subpath_file(const char *path, const char *f)
}
EXPORT_SYMBOL(concat_subpath_file);
+/**
+ * find_path - find a file in a colon separated path
+ * @path: The search path, colon separated
+ * @filename: The filename to search for
+ * @filter: filter function
+ *
+ * searches for @filename in a colon separated list of directories given in
+ * @path. @filter should return true when the current file matches the expectations,
+ * false otherwise. @filter should check for existence of the file, but could also
+ * check for additional flags.
+ */
+char *find_path(const char *path, const char *filename,
+ bool (*filter)(const char *))
+{
+ char *p, *n, *freep;
+
+ freep = p = strdup(path);
+ while (p) {
+ n = strchr(p, ':');
+ if (n)
+ *n++ = '\0';
+ if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
+ p = concat_path_file(p, filename);
+ if (filter(p)) {
+ free(freep);
+ return p;
+ }
+ free(p);
+ }
+ p = n;
+ }
+ free(freep);
+ return NULL;
+}
+EXPORT_SYMBOL(find_path);
+
/* check if path points to an executable file;
* return 1 if found;
* return 0 otherwise;
*/
-int execable_file(const char *name)
+bool execable_file(const char *name)
{
struct stat s;
return (!stat(name, &s) && S_ISREG(s.st_mode));
@@ -67,25 +103,7 @@ EXPORT_SYMBOL(execable_file);
*/
char *find_execable(const char *filename)
{
- char *path, *p, *n;
-
- p = path = strdup(getenv("PATH"));
- while (p) {
- n = strchr(p, ':');
- if (n)
- *n++ = '\0';
- if (*p != '\0') { /* it's not a PATH="foo::bar" situation */
- p = concat_path_file(p, filename);
- if (execable_file(p)) {
- free(path);
- return p;
- }
- free(p);
- }
- p = n;
- }
- free(path);
- return NULL;
+ return find_path(getenv("PATH"), filename, execable_file);
}
EXPORT_SYMBOL(find_execable);
diff --git a/lib/libfile.c b/lib/libfile.c
index b4d87b624a..67fc9cc7f3 100644
--- a/lib/libfile.c
+++ b/lib/libfile.c
@@ -33,10 +33,8 @@ int pwrite_full(int fd, const void *buf, size_t size, loff_t offset)
while (size) {
now = pwrite(fd, buf, size, offset);
- if (now == 0) {
- errno = ENOSPC;
- return -errno;
- }
+ if (now == 0)
+ return errno_set(-ENOSPC);
if (now < 0)
return now;
size -= now;
@@ -61,10 +59,8 @@ int write_full(int fd, const void *buf, size_t size)
while (size) {
now = write(fd, buf, size);
- if (now == 0) {
- errno = ENOSPC;
- return -errno;
- }
+ if (now == 0)
+ return errno_set(-ENOSPC);
if (now < 0)
return now;
size -= now;
@@ -76,6 +72,31 @@ int write_full(int fd, const void *buf, size_t size)
EXPORT_SYMBOL(write_full);
/*
+ * pread_full - read to filedescriptor at offset
+ *
+ * Like pread, but this function only returns less bytes than
+ * requested when the end of file is reached.
+ */
+int pread_full(int fd, void *buf, size_t size, loff_t offset)
+{
+ size_t insize = size;
+ int now;
+
+ while (size) {
+ now = pread(fd, buf, size, offset);
+ if (now == 0)
+ break;
+ if (now < 0)
+ return now;
+ size -= now;
+ buf += now;
+ }
+
+ return insize - size;
+}
+EXPORT_SYMBOL(pread_full);
+
+/*
* read_full - read from filedescriptor
*
* Like read, but this function only returns less bytes than
@@ -100,6 +121,29 @@ int read_full(int fd, void *buf, size_t size)
}
EXPORT_SYMBOL(read_full);
+int copy_fd(int in, int out)
+{
+ int bs = 4096, ret;
+ void *buf = malloc(bs);
+
+ if (!buf)
+ return -ENOMEM;
+
+ while (1) {
+ ret = read(in, buf, bs);
+ if (ret <= 0)
+ break;
+
+ ret = write_full(out, buf, ret);
+ if (ret < 0)
+ break;
+ }
+
+ free(buf);
+
+ return ret;
+}
+
/*
* read_file_line - read a line from a file
*
@@ -114,7 +158,6 @@ char *read_file_line(const char *fmt, ...)
va_list args;
char *filename;
char *buf, *line = NULL;
- size_t size;
int ret;
struct stat s;
@@ -129,7 +172,7 @@ char *read_file_line(const char *fmt, ...)
if (s.st_size > 1024)
goto out;
- buf = read_file(filename, &size);
+ buf = read_file(filename, NULL);
if (!buf)
goto out;
@@ -144,6 +187,33 @@ out:
EXPORT_SYMBOL_GPL(read_file_line);
/**
+ * read_file_into_buf - read a file to an external buffer
+ * @filename: The filename to read
+ * @buf: The buffer to read into
+ * @size: The buffer size
+ *
+ * This function reads a file to an external buffer. At maximum @size
+ * bytes are read.
+ *
+ * Return: number of bytes read, or negative error code.
+ */
+ssize_t read_file_into_buf(const char *filename, void *buf, size_t size)
+{
+ int fd;
+ ssize_t ret;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ return fd;
+
+ ret = read_full(fd, buf, size);
+
+ close(fd);
+
+ return ret;
+}
+
+/**
* read_file_2 - read a file to an allocated buffer
* @filename: The filename to read
* @size: After successful return contains the size of the file
@@ -165,11 +235,10 @@ EXPORT_SYMBOL_GPL(read_file_line);
int read_file_2(const char *filename, size_t *size, void **outbuf,
loff_t max_size)
{
- int fd;
struct stat s;
void *buf = NULL;
const char *tmpfile = "/.read_file_tmp";
- int ret;
+ ssize_t ret;
loff_t read_size;
again:
@@ -190,24 +259,16 @@ again:
goto again;
}
- buf = calloc(read_size + 1, 1);
+ /* ensure wchar_t nul termination */
+ buf = calloc(ALIGN(read_size, 2) + 2, 1);
if (!buf) {
- ret = -ENOMEM;
- errno = ENOMEM;
+ ret = errno_set(-ENOMEM);
goto err_out;
}
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
- ret = fd;
- goto err_out;
- }
-
- ret = read_full(fd, buf, read_size);
+ ret = read_file_into_buf(filename, buf, read_size);
if (ret < 0)
- goto err_out1;
-
- close(fd);
+ goto err_out;
if (size)
*size = ret;
@@ -222,8 +283,6 @@ again:
return 0;
-err_out1:
- close(fd);
err_out:
free(buf);
@@ -259,6 +318,56 @@ void *read_file(const char *filename, size_t *size)
EXPORT_SYMBOL(read_file);
/**
+ * read_fd - read from a file descriptor to an allocated buffer
+ * @filename: The file descriptor to read
+ * @size: After successful return contains the size of the file
+ *
+ * This function reads a file descriptor from offset 0 until EOF to an
+ * allocated buffer.
+ *
+ * Return: On success, returns a nul-terminated buffer with the file's
+ * contents that should be deallocated with free().
+ * On error, NULL is returned and errno is set to an error code.
+ */
+void *read_fd(int fd, size_t *out_size)
+{
+ struct stat st;
+ ssize_t ret;
+ void *buf;
+
+ ret = fstat(fd, &st);
+ if (ret < 0)
+ return NULL;
+
+ if (st.st_size == FILE_SIZE_STREAM) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* For user convenience, we always nul-terminate the buffer in
+ * case it contains a string. As we don't want to assume the string
+ * to be either an array of char or wchar_t, we just unconditionally
+ * add 2 bytes as terminator. As the two byte terminator needs to be
+ * aligned, we just make it three bytes
+ */
+ buf = malloc(st.st_size + 3);
+ if (!buf)
+ return NULL;
+
+ ret = pread_full(fd, buf, st.st_size, 0);
+ if (ret < 0) {
+ free(buf);
+ return NULL;
+ }
+
+ memset(buf + st.st_size, '\0', 3);
+ *out_size = st.st_size;
+
+ return buf;
+}
+EXPORT_SYMBOL(read_fd);
+
+/**
* write_file - write a buffer to a file
* @filename: The filename to write
* @size: The size of the buffer
@@ -332,14 +441,14 @@ int copy_file(const char *src, const char *dst, int verbose)
int r, s;
int ret = 1, err1 = 0;
int mode;
- int total = 0;
+ loff_t total = 0;
struct stat srcstat, dststat;
rw_buf = xmalloc(RW_BUF_SIZE);
srcfd = open(src, O_RDONLY);
if (srcfd < 0) {
- printf("could not open %s: %s\n", src, errno_str());
+ printf("could not open %s: %m\n", src);
ret = srcfd;
goto out;
}
@@ -348,30 +457,37 @@ int copy_file(const char *src, const char *dst, int verbose)
s = stat(dst, &dststat);
if (s && s != -ENOENT) {
- printf("could not stat %s: %s\n", dst, errno_str());
+ printf("could not stat %s: %m\n", dst);
ret = s;
goto out;
}
- /* Set O_TRUNC only if file exist and is a regular file */
+ /* Set O_TRUNC only if file exists and is a regular file */
if (!s && S_ISREG(dststat.st_mode))
mode |= O_TRUNC;
dstfd = open(dst, mode);
if (dstfd < 0) {
- printf("could not open %s: %s\n", dst, errno_str());
+ printf("could not open %s: %m\n", dst);
ret = dstfd;
goto out;
}
- discard_range(dstfd, srcstat.st_size, 0);
+ ret = stat(src, &srcstat);
+ if (ret)
+ goto out;
- if (verbose) {
- if (stat(src, &srcstat) < 0)
- srcstat.st_size = 0;
+ if (srcstat.st_size != FILESIZE_MAX) {
+ discard_range(dstfd, srcstat.st_size, 0);
+ if (s || S_ISREG(dststat.st_mode)) {
+ ret = ftruncate(dstfd, srcstat.st_size);
+ if (ret)
+ goto out;
+ }
+ }
+ if (verbose)
init_progression_bar(srcstat.st_size);
- }
while (1) {
r = read(srcfd, rw_buf, RW_BUF_SIZE);
@@ -487,8 +603,10 @@ int compare_file(const char *f1, const char *f2)
if (ret)
goto err_out2;
- if (s1.st_size != s2.st_size)
- return 1;
+ if (s1.st_size != s2.st_size) {
+ ret = 1;
+ goto err_out2;
+ }
buf1 = xmalloc(RW_BUF_SIZE);
buf2 = xmalloc(RW_BUF_SIZE);
@@ -532,17 +650,15 @@ err_out1:
* @pos: The position to lseek to
*
* Return: If successful this function returns a positive
- * filedescriptor number, otherwise -1 is returned
+ * filedescriptor number, otherwise a negative error code is returned
*/
int open_and_lseek(const char *filename, int mode, loff_t pos)
{
- int fd;
+ int fd, ret;
fd = open(filename, mode);
- if (fd < 0) {
- perror("open");
+ if (fd < 0)
return fd;
- }
if (!pos)
return fd;
@@ -550,26 +666,26 @@ int open_and_lseek(const char *filename, int mode, loff_t pos)
if (mode & (O_WRONLY | O_RDWR)) {
struct stat s;
- if (fstat(fd, &s)) {
- perror("fstat");
+ ret = fstat(fd, &s);
+ if (ret < 0)
goto out;
- }
- if (s.st_size < pos && ftruncate(fd, pos)) {
- perror("ftruncate");
- goto out;
+ if (s.st_size < pos) {
+ ret = ftruncate(fd, pos);
+ if (ret)
+ goto out;
}
}
if (lseek(fd, pos, SEEK_SET) != pos) {
- perror("lseek");
+ ret = -errno;
goto out;
}
return fd;
out:
close(fd);
- return -1;
+ return ret;
}
/**
diff --git a/lib/libscan.c b/lib/libscan.c
index c4139e69d1..ad2e3df341 100644
--- a/lib/libscan.c
+++ b/lib/libscan.c
@@ -31,7 +31,7 @@
#include <mtd/ubi-user.h>
#include <mtd/utils.h>
#include <mtd/ubi-media.h>
-#include <asm-generic/div64.h>
+#include <linux/math64.h>
int libscan_ubi_scan(struct mtd_info *mtd, struct ubi_scan_info **info,
int verbose)
diff --git a/lib/list_debug.c b/lib/list_debug.c
new file mode 100644
index 0000000000..7de4c709a3
--- /dev/null
+++ b/lib/list_debug.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2006, Red Hat, Inc., Dave Jones
+ * Released under the General Public License (GPL).
+ *
+ * This file contains the linked list validation for DEBUG_LIST.
+ */
+
+#include <linux/export.h>
+#include <linux/list.h>
+#include <linux/bug.h>
+#include <linux/kernel.h>
+
+/*
+ * Check that the data structures for the list manipulations are reasonably
+ * valid. Failures here indicate memory corruption (and possibly an exploit
+ * attempt).
+ */
+
+bool __list_add_valid_or_report(struct list_head *new, struct list_head *prev,
+ struct list_head *next)
+{
+ if (CHECK_DATA_CORRUPTION(prev == NULL,
+ "list_add corruption. prev is NULL.\n") ||
+ CHECK_DATA_CORRUPTION(next == NULL,
+ "list_add corruption. next is NULL.\n") ||
+ CHECK_DATA_CORRUPTION(next->prev != prev,
+ "list_add corruption. next->prev should be prev (%px), but was %px. (next=%px).\n",
+ prev, next->prev, next) ||
+ CHECK_DATA_CORRUPTION(prev->next != next,
+ "list_add corruption. prev->next should be next (%px), but was %px. (prev=%px).\n",
+ next, prev->next, prev) ||
+ CHECK_DATA_CORRUPTION(new == prev || new == next,
+ "list_add double add: new=%px, prev=%px, next=%px.\n",
+ new, prev, next))
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL(__list_add_valid_or_report);
+
+bool __list_del_entry_valid_or_report(struct list_head *entry)
+{
+ struct list_head *prev, *next;
+
+ prev = entry->prev;
+ next = entry->next;
+
+ if (CHECK_DATA_CORRUPTION(next == NULL,
+ "list_del corruption, %px->next is NULL\n", entry) ||
+ CHECK_DATA_CORRUPTION(prev == NULL,
+ "list_del corruption, %px->prev is NULL\n", entry) ||
+ CHECK_DATA_CORRUPTION(next == LIST_POISON1,
+ "list_del corruption, %px->next is LIST_POISON1 (%px)\n",
+ entry, LIST_POISON1) ||
+ CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
+ "list_del corruption, %px->prev is LIST_POISON2 (%px)\n",
+ entry, LIST_POISON2) ||
+ CHECK_DATA_CORRUPTION(prev->next != entry,
+ "list_del corruption. prev->next should be %px, but was %px. (prev=%px)\n",
+ entry, prev->next, prev) ||
+ CHECK_DATA_CORRUPTION(next->prev != entry,
+ "list_del corruption. next->prev should be %px, but was %px. (next=%px)\n",
+ entry, next->prev, next))
+ return false;
+
+ return true;
+}
+EXPORT_SYMBOL(__list_del_entry_valid_or_report);
diff --git a/lib/list_sort.c b/lib/list_sort.c
index 84c6f6465b..eea87f834c 100644
--- a/lib/list_sort.c
+++ b/lib/list_sort.c
@@ -1,7 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#ifndef __BAREBOX__
#include <linux/kernel.h>
#define EXPORT_SYMBOL(x)
#else
+#define pr_fmt(fmt) "list_sort: " fmt
#include <common.h>
#include <malloc.h>
#endif
@@ -127,9 +130,7 @@ void list_sort(void *priv, struct list_head *head,
}
if (lev > max_lev) {
if (unlikely(lev >= ARRAY_SIZE(part)-1)) {
- printk_once(KERN_DEBUG "list passed to"
- " list_sort() too long for"
- " efficiency\n");
+ pr_debug_once("list passed to too long for efficient sorting\n");
lev--;
}
max_lev = lev;
@@ -144,152 +145,3 @@ void list_sort(void *priv, struct list_head *head,
merge_and_restore_back_links(priv, cmp, head, part[max_lev], list);
}
EXPORT_SYMBOL(list_sort);
-
-#ifdef CONFIG_TEST_LIST_SORT
-
-#include <linux/random.h>
-
-/*
- * The pattern of set bits in the list length determines which cases
- * are hit in list_sort().
- */
-#define TEST_LIST_LEN (512+128+2) /* not including head */
-
-#define TEST_POISON1 0xDEADBEEF
-#define TEST_POISON2 0xA324354C
-
-struct debug_el {
- unsigned int poison1;
- struct list_head list;
- unsigned int poison2;
- int value;
- unsigned serial;
-};
-
-/* Array, containing pointers to all elements in the test list */
-static struct debug_el **elts __initdata;
-
-static int __init check(struct debug_el *ela, struct debug_el *elb)
-{
- if (ela->serial >= TEST_LIST_LEN) {
- printk(KERN_ERR "list_sort_test: error: incorrect serial %d\n",
- ela->serial);
- return -EINVAL;
- }
- if (elb->serial >= TEST_LIST_LEN) {
- printk(KERN_ERR "list_sort_test: error: incorrect serial %d\n",
- elb->serial);
- return -EINVAL;
- }
- if (elts[ela->serial] != ela || elts[elb->serial] != elb) {
- printk(KERN_ERR "list_sort_test: error: phantom element\n");
- return -EINVAL;
- }
- if (ela->poison1 != TEST_POISON1 || ela->poison2 != TEST_POISON2) {
- printk(KERN_ERR "list_sort_test: error: bad poison: %#x/%#x\n",
- ela->poison1, ela->poison2);
- return -EINVAL;
- }
- if (elb->poison1 != TEST_POISON1 || elb->poison2 != TEST_POISON2) {
- printk(KERN_ERR "list_sort_test: error: bad poison: %#x/%#x\n",
- elb->poison1, elb->poison2);
- return -EINVAL;
- }
- return 0;
-}
-
-static int __init cmp(void *priv, struct list_head *a, struct list_head *b)
-{
- struct debug_el *ela, *elb;
-
- ela = container_of(a, struct debug_el, list);
- elb = container_of(b, struct debug_el, list);
-
- check(ela, elb);
- return ela->value - elb->value;
-}
-
-static int __init list_sort_test(void)
-{
- int i, count = 1, err = -EINVAL;
- struct debug_el *el;
- struct list_head *cur, *tmp;
- LIST_HEAD(head);
-
- printk(KERN_DEBUG "list_sort_test: start testing list_sort()\n");
-
- elts = kmalloc(sizeof(void *) * TEST_LIST_LEN, GFP_KERNEL);
- if (!elts) {
- printk(KERN_ERR "list_sort_test: error: cannot allocate "
- "memory\n");
- goto exit;
- }
-
- for (i = 0; i < TEST_LIST_LEN; i++) {
- el = kmalloc(sizeof(*el), GFP_KERNEL);
- if (!el) {
- printk(KERN_ERR "list_sort_test: error: cannot "
- "allocate memory\n");
- goto exit;
- }
- /* force some equivalencies */
- el->value = prandom_u32() % (TEST_LIST_LEN / 3);
- el->serial = i;
- el->poison1 = TEST_POISON1;
- el->poison2 = TEST_POISON2;
- elts[i] = el;
- list_add_tail(&el->list, &head);
- }
-
- list_sort(NULL, &head, cmp);
-
- for (cur = head.next; cur->next != &head; cur = cur->next) {
- struct debug_el *el1;
- int cmp_result;
-
- if (cur->next->prev != cur) {
- printk(KERN_ERR "list_sort_test: error: list is "
- "corrupted\n");
- goto exit;
- }
-
- cmp_result = cmp(NULL, cur, cur->next);
- if (cmp_result > 0) {
- printk(KERN_ERR "list_sort_test: error: list is not "
- "sorted\n");
- goto exit;
- }
-
- el = container_of(cur, struct debug_el, list);
- el1 = container_of(cur->next, struct debug_el, list);
- if (cmp_result == 0 && el->serial >= el1->serial) {
- printk(KERN_ERR "list_sort_test: error: order of "
- "equivalent elements not preserved\n");
- goto exit;
- }
-
- if (check(el, el1)) {
- printk(KERN_ERR "list_sort_test: error: element check "
- "failed\n");
- goto exit;
- }
- count++;
- }
-
- if (count != TEST_LIST_LEN) {
- printk(KERN_ERR "list_sort_test: error: bad list length %d",
- count);
- goto exit;
- }
-
- err = 0;
-exit:
- kfree(elts);
- list_for_each_safe(cur, tmp, &head) {
- list_del(cur);
- kfree(container_of(cur, struct debug_el, list));
- }
- return err;
-}
-module_init(list_sort_test);
-#endif /* CONFIG_TEST_LIST_SORT */
diff --git a/lib/logo/.gitignore b/lib/logo/.gitignore
index 0d5475cb78..62ee7d270e 100644
--- a/lib/logo/.gitignore
+++ b/lib/logo/.gitignore
@@ -1,10 +1,7 @@
-barebox-logo-w64.bblogo
-barebox-logo-w64.bblogo.S
-barebox-logo-w240.bblogo
-barebox-logo-w240.bblogo.S
-barebox-logo-w320.bblogo
-barebox-logo-w320.bblogo.S
-barebox-logo-w400.bblogo
-barebox-logo-w400.bblogo.S
-barebox-logo-w640.bblogo
-barebox-logo-w640.bblogo.S
+# SPDX-License-Identifier: GPL-2.0-only
+
+barebox-logo-*.bblogo
+barebox-logo-*.bblogo.S
+barebox-logo-*.bmp
+barebox-logo-*.png
+barebox-logo-*.qoi
diff --git a/lib/logo/Kconfig b/lib/logo/Kconfig
index 0718053e5c..7e5a6fcb63 100644
--- a/lib/logo/Kconfig
+++ b/lib/logo/Kconfig
@@ -1,12 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
menuconfig BAREBOX_LOGO
bool "include barebox logos in build"
+ depends on IMAGE_RENDERER
help
- Say yes here to build the barebox logos. This adds inkscape to the build
- dependencies. The logo can be found under /logo/barebox-logo-<width>.png
- in the running barebox.
+ Say yes here to build the barebox logos. This adds ImageMagick's
+ convert tool to the build dependencies. The logo can be found under
+ /logo/barebox-logo-<width>.<ext> in the running barebox.
if BAREBOX_LOGO
+choice
+ prompt "Logo image encoding format to be used"
+ default BAREBOX_LOGO_PNG
+
+config BAREBOX_LOGO_PNG
+ bool "png"
+ select PNG
+ help
+ Encode logo with the PNG image format
+
+config BAREBOX_LOGO_BMP
+ bool "bmp"
+ select BMP
+ help
+ Encode logo with the BMP image format
+
+config BAREBOX_LOGO_QOI
+ bool "qoi"
+ select QOI
+ select QOICONV
+ help
+ Encode logo with the QOI image format
+
+endchoice
+
config BAREBOX_LOGO_64
bool "include 64x32 pixel logo"
diff --git a/lib/logo/Makefile b/lib/logo/Makefile
index eb7aee080e..9c14105e88 100644
--- a/lib/logo/Makefile
+++ b/lib/logo/Makefile
@@ -1,17 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
-OPTS_barebox-logo-w64.bblogo="-w 64"
+OPTS_barebox-logo-w64 = -resize 64
bblogo-$(CONFIG_BAREBOX_LOGO_64) += barebox-logo-w64
-OPTS_barebox-logo-w240.bblogo="-w 240"
+OPTS_barebox-logo-w240 = -resize 240
bblogo-$(CONFIG_BAREBOX_LOGO_240) += barebox-logo-w240
-OPTS_barebox-logo-w320.bblogo="-w 320"
+OPTS_barebox-logo-w320 = -resize 320
bblogo-$(CONFIG_BAREBOX_LOGO_320) += barebox-logo-w320
-OPTS_barebox-logo-w400.bblogo="-w 400"
+OPTS_barebox-logo-w400 = -resize 400
bblogo-$(CONFIG_BAREBOX_LOGO_400) += barebox-logo-w400
-OPTS_barebox-logo-w640.bblogo="-w 640"
+OPTS_barebox-logo-w640 = -resize 640
bblogo-$(CONFIG_BAREBOX_LOGO_640) += barebox-logo-w640
obj-y += $(patsubst %,%.bblogo.o,$(bblogo-y))
@@ -21,10 +22,11 @@ extra-y += $(patsubst %,%.bblogo.o,$(bblogo-y))
obj-$(CONFIG_BAREBOX_LOGO) += logo.o
-quiet_cmd_logo_S = LOGO.S $@
+quiet_cmd_logo_S = LOGO.S $@
cmd_logo_S = \
( \
- echo '\#include <asm-generic/barebox.lds.h>'; \
+ echo '\#include <asm/barebox.lds.h>'; \
+ echo '.section .note.GNU-stack,"",%progbits'; \
echo '.section .bblogo.rodata.$(subst -,_,$(*F)),"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
echo '.global __bblogo_$(subst -,_,$(*F))_start'; \
@@ -38,13 +40,44 @@ cmd_logo_S = \
%.bblogo.S: %.bblogo FORCE
$(call if_changed,logo_S)
-quiet_cmd_logo = LOGO.S $@
-cmd_logo = \
-( \
- inkscape -z $(OPTS_$(@F)) -e $@ $< > /dev/null; \
-)
+quiet_cmd_logo = LOGO $@
+ cmd_logo = cp $< $@
-%.bblogo: $(srctree)/Documentation/barebox.svg FORCE
+ifdef CONFIG_BAREBOX_LOGO_PNG
+extra-y += $(patsubst %,%.png,$(bblogo-y))
+%.bblogo: %.png FORCE
+ $(call if_changed,logo)
+endif
+ifdef CONFIG_BAREBOX_LOGO_BMP
+extra-y += $(patsubst %,%.bmp,$(bblogo-y))
+%.bblogo: %.bmp FORCE
+ $(call if_changed,logo)
+endif
+ifdef CONFIG_BAREBOX_LOGO_QOI
+extra-y += $(patsubst %,%.png,$(bblogo-y))
+extra-y += $(patsubst %,%.qoi,$(bblogo-y))
+%.bblogo: %.qoi FORCE
$(call if_changed,logo)
+endif
+
+CONVERTOPTS += -background none
+
+quiet_cmd_bmp = BMP $@
+ cmd_bmp = convert $(OPTS_$(@F:.bmp=)) $(CONVERTOPTS) $< bmp:$@
+
+%.bmp: $(srctree)/Documentation/barebox.svg FORCE
+ $(call if_changed,bmp)
+
+quiet_cmd_png = PNG $@
+ cmd_png = convert $(OPTS_$(@F:.png=)) $(CONVERTOPTS) $< png:$@
+
+%.png: $(srctree)/Documentation/barebox.svg FORCE
+ $(call if_changed,png)
+
+quiet_cmd_qoi = QOI $@
+ cmd_qoi = $(objtree)/scripts/qoiconv $< $@
+
+%.qoi: %.png FORCE
+ $(call if_changed,qoi)
-clean-files += *.bblogo *.bblogo.S
+clean-files += *.png *.qoi *.bmp *.bblogo *.bblogo.S
diff --git a/lib/logo/logo.c b/lib/logo/logo.c
index 8349b060ac..a64f489939 100644
--- a/lib/logo/logo.c
+++ b/lib/logo/logo.c
@@ -32,8 +32,16 @@ static void load_logo(int width, void *start, void *end)
{
char *filename;
size_t size = end - start;
+ char *ext = "";
- filename = basprintf("/logo/barebox-logo-%d.png", width);
+ if (IS_ENABLED(CONFIG_BAREBOX_LOGO_PNG))
+ ext = "png";
+ else if (IS_ENABLED(CONFIG_BAREBOX_LOGO_BMP))
+ ext = "bmp";
+ else if (IS_ENABLED(CONFIG_BAREBOX_LOGO_QOI))
+ ext = "qoi";
+
+ filename = basprintf("/logo/barebox-logo-%d.%s", width, ext);
write_file(filename, start, size);
free(filename);
}
diff --git a/lib/lz4/Makefile b/lib/lz4/Makefile
index 7f548c6d1c..7156ec9145 100644
--- a/lib/lz4/Makefile
+++ b/lib/lz4/Makefile
@@ -1 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-$(CONFIG_LZ4_DECOMPRESS) += lz4_decompress.o
diff --git a/lib/lzo/Kconfig b/lib/lzo/Kconfig
index 9276b2128a..2ead1321e8 100644
--- a/lib/lzo/Kconfig
+++ b/lib/lzo/Kconfig
@@ -1,7 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
config LZO_DECOMPRESS
bool "include lzo uncompression support"
select UNCOMPRESS
-
-config LZO_COMPRESS
- bool
-
diff --git a/lib/lzo/Makefile b/lib/lzo/Makefile
index f2191f4a3d..4ab28ff6f5 100644
--- a/lib/lzo/Makefile
+++ b/lib/lzo/Makefile
@@ -1,4 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_LZO_COMPRESS) += lzo1x_compress.o
obj-$(CONFIG_LZO_DECOMPRESS) += lzo1x_decompress_safe.o
-
diff --git a/lib/lzo/lzo1x_compress.c b/lib/lzo/lzo1x_compress.c
deleted file mode 100644
index 236eb21167..0000000000
--- a/lib/lzo/lzo1x_compress.c
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * LZO1X Compressor from LZO
- *
- * Copyright (C) 1996-2012 Markus F.X.J. Oberhumer <markus@oberhumer.com>
- *
- * The full LZO package can be found at:
- * http://www.oberhumer.com/opensource/lzo/
- *
- * Changed for Linux kernel use by:
- * Nitin Gupta <nitingupta910@gmail.com>
- * Richard Purdie <rpurdie@openedhand.com>
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <asm/unaligned.h>
-#include <linux/lzo.h>
-#include "lzodefs.h"
-
-static noinline size_t
-lzo1x_1_do_compress(const unsigned char *in, size_t in_len,
- unsigned char *out, size_t *out_len,
- size_t ti, void *wrkmem)
-{
- const unsigned char *ip;
- unsigned char *op;
- const unsigned char * const in_end = in + in_len;
- const unsigned char * const ip_end = in + in_len - 20;
- const unsigned char *ii;
- lzo_dict_t * const dict = (lzo_dict_t *) wrkmem;
-
- op = out;
- ip = in;
- ii = ip;
- ip += ti < 4 ? 4 - ti : 0;
-
- for (;;) {
- const unsigned char *m_pos;
- size_t t, m_len, m_off;
- u32 dv;
-literal:
- ip += 1 + ((ip - ii) >> 5);
-next:
- if (unlikely(ip >= ip_end))
- break;
- dv = get_unaligned_le32(ip);
- t = ((dv * 0x1824429d) >> (32 - D_BITS)) & D_MASK;
- m_pos = in + dict[t];
- dict[t] = (lzo_dict_t) (ip - in);
- if (unlikely(dv != get_unaligned_le32(m_pos)))
- goto literal;
-
- ii -= ti;
- ti = 0;
- t = ip - ii;
- if (t != 0) {
- if (t <= 3) {
- op[-2] |= t;
- COPY4(op, ii);
- op += t;
- } else if (t <= 16) {
- *op++ = (t - 3);
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
- op += t;
- } else {
- if (t <= 18) {
- *op++ = (t - 3);
- } else {
- size_t tt = t - 18;
- *op++ = 0;
- while (unlikely(tt > 255)) {
- tt -= 255;
- *op++ = 0;
- }
- *op++ = tt;
- }
- do {
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
- op += 16;
- ii += 16;
- t -= 16;
- } while (t >= 16);
- if (t > 0) do {
- *op++ = *ii++;
- } while (--t > 0);
- }
- }
-
- m_len = 4;
- {
-#if defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ64)
- u64 v;
- v = get_unaligned((const u64 *) (ip + m_len)) ^
- get_unaligned((const u64 *) (m_pos + m_len));
- if (unlikely(v == 0)) {
- do {
- m_len += 8;
- v = get_unaligned((const u64 *) (ip + m_len)) ^
- get_unaligned((const u64 *) (m_pos + m_len));
- if (unlikely(ip + m_len >= ip_end))
- goto m_len_done;
- } while (v == 0);
- }
-# if defined(__LITTLE_ENDIAN)
- m_len += (unsigned) __builtin_ctzll(v) / 8;
-# elif defined(__BIG_ENDIAN)
- m_len += (unsigned) __builtin_clzll(v) / 8;
-# else
-# error "missing endian definition"
-# endif
-#elif defined(CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS) && defined(LZO_USE_CTZ32)
- u32 v;
- v = get_unaligned((const u32 *) (ip + m_len)) ^
- get_unaligned((const u32 *) (m_pos + m_len));
- if (unlikely(v == 0)) {
- do {
- m_len += 4;
- v = get_unaligned((const u32 *) (ip + m_len)) ^
- get_unaligned((const u32 *) (m_pos + m_len));
- if (v != 0)
- break;
- m_len += 4;
- v = get_unaligned((const u32 *) (ip + m_len)) ^
- get_unaligned((const u32 *) (m_pos + m_len));
- if (unlikely(ip + m_len >= ip_end))
- goto m_len_done;
- } while (v == 0);
- }
-# if defined(__LITTLE_ENDIAN)
- m_len += (unsigned) __builtin_ctz(v) / 8;
-# elif defined(__BIG_ENDIAN)
- m_len += (unsigned) __builtin_clz(v) / 8;
-# else
-# error "missing endian definition"
-# endif
-#else
- if (unlikely(ip[m_len] == m_pos[m_len])) {
- do {
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (ip[m_len] != m_pos[m_len])
- break;
- m_len += 1;
- if (unlikely(ip + m_len >= ip_end))
- goto m_len_done;
- } while (ip[m_len] == m_pos[m_len]);
- }
-#endif
- }
-m_len_done:
-
- m_off = ip - m_pos;
- ip += m_len;
- ii = ip;
- if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) {
- m_off -= 1;
- *op++ = (((m_len - 1) << 5) | ((m_off & 7) << 2));
- *op++ = (m_off >> 3);
- } else if (m_off <= M3_MAX_OFFSET) {
- m_off -= 1;
- if (m_len <= M3_MAX_LEN)
- *op++ = (M3_MARKER | (m_len - 2));
- else {
- m_len -= M3_MAX_LEN;
- *op++ = M3_MARKER | 0;
- while (unlikely(m_len > 255)) {
- m_len -= 255;
- *op++ = 0;
- }
- *op++ = (m_len);
- }
- *op++ = (m_off << 2);
- *op++ = (m_off >> 6);
- } else {
- m_off -= 0x4000;
- if (m_len <= M4_MAX_LEN)
- *op++ = (M4_MARKER | ((m_off >> 11) & 8)
- | (m_len - 2));
- else {
- m_len -= M4_MAX_LEN;
- *op++ = (M4_MARKER | ((m_off >> 11) & 8));
- while (unlikely(m_len > 255)) {
- m_len -= 255;
- *op++ = 0;
- }
- *op++ = (m_len);
- }
- *op++ = (m_off << 2);
- *op++ = (m_off >> 6);
- }
- goto next;
- }
- *out_len = op - out;
- return in_end - (ii - ti);
-}
-
-int lzo1x_1_compress(const unsigned char *in, size_t in_len,
- unsigned char *out, size_t *out_len,
- void *wrkmem)
-{
- const unsigned char *ip = in;
- unsigned char *op = out;
- size_t l = in_len;
- size_t t = 0;
-
- while (l > 20) {
- size_t ll = l <= (M4_MAX_OFFSET + 1) ? l : (M4_MAX_OFFSET + 1);
- uintptr_t ll_end = (uintptr_t) ip + ll;
- if ((ll_end + ((t + ll) >> 5)) <= ll_end)
- break;
- BUILD_BUG_ON(D_SIZE * sizeof(lzo_dict_t) > LZO1X_1_MEM_COMPRESS);
- memset(wrkmem, 0, D_SIZE * sizeof(lzo_dict_t));
- t = lzo1x_1_do_compress(ip, ll, op, out_len, t, wrkmem);
- ip += ll;
- op += *out_len;
- l -= ll;
- }
- t += l;
-
- if (t > 0) {
- const unsigned char *ii = in + in_len - t;
-
- if (op == out && t <= 238) {
- *op++ = (17 + t);
- } else if (t <= 3) {
- op[-2] |= t;
- } else if (t <= 18) {
- *op++ = (t - 3);
- } else {
- size_t tt = t - 18;
- *op++ = 0;
- while (tt > 255) {
- tt -= 255;
- *op++ = 0;
- }
- *op++ = tt;
- }
- if (t >= 16) do {
- COPY8(op, ii);
- COPY8(op + 8, ii + 8);
- op += 16;
- ii += 16;
- t -= 16;
- } while (t >= 16);
- if (t > 0) do {
- *op++ = *ii++;
- } while (--t > 0);
- }
-
- *op++ = M4_MARKER | 1;
- *op++ = 0;
- *op++ = 0;
-
- *out_len = op - out;
- return LZO_E_OK;
-}
-EXPORT_SYMBOL_GPL(lzo1x_1_compress);
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("LZO1X-1 Compressor");
diff --git a/lib/make_directory.c b/lib/make_directory.c
index 29d08cf536..59ac87e6bf 100644
--- a/lib/make_directory.c
+++ b/lib/make_directory.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <string.h>
#include <errno.h>
@@ -18,6 +19,9 @@ STATIC int make_directory(const char *dir)
char c;
int ret = 0;
+ if (!s)
+ return -ENOMEM;
+
do {
c = 0;
diff --git a/lib/math/Makefile b/lib/math/Makefile
new file mode 100644
index 0000000000..2830dedb2f
--- /dev/null
+++ b/lib/math/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+obj-y += div64.o
+pbl-y += div64.o
+obj-y += rational.o
diff --git a/lib/math/div64.c b/lib/math/div64.c
new file mode 100644
index 0000000000..386497592b
--- /dev/null
+++ b/lib/math/div64.c
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2003 Bernardo Innocenti <bernie@develer.com>
+ *
+ * Based on former do_div() implementation from asm-parisc/div64.h:
+ * Copyright (C) 1999 Hewlett-Packard Co
+ * Copyright (C) 1999 David Mosberger-Tang <davidm@hpl.hp.com>
+ *
+ *
+ * Generic C version of 64bit/32bit division and modulo, with
+ * 64bit result and 32bit remainder.
+ *
+ * The fast case for (n>>32 == 0) is handled inline by do_div().
+ *
+ * Code generated for this function might be very inefficient
+ * for some CPUs. __div64_32() can be overridden by linking arch-specific
+ * assembly versions such as arch/ppc/lib/div64.S and arch/sh/lib/div64.S
+ * or by defining a preprocessor macro in arch/include/asm/div64.h.
+ */
+
+#include <linux/bitops.h>
+#include <module.h>
+#include <linux/kernel.h>
+#include <linux/math64.h>
+#include <linux/log2.h>
+
+/* Not needed on 64bit architectures */
+#if BITS_PER_LONG == 32
+
+#ifndef __div64_32
+uint32_t __attribute__((weak)) __div64_32(uint64_t *n, uint32_t base)
+{
+ uint64_t rem = *n;
+ uint64_t b = base;
+ uint64_t res, d = 1;
+ uint32_t high = rem >> 32;
+
+ /* Reduce the thing a bit first */
+ res = 0;
+ if (high >= base) {
+ high /= base;
+ res = (uint64_t) high << 32;
+ rem -= (uint64_t) (high*base) << 32;
+ }
+
+ while ((int64_t)b > 0 && b < rem) {
+ b = b+b;
+ d = d+d;
+ }
+
+ do {
+ if (rem >= b) {
+ rem -= b;
+ res += d;
+ }
+ b >>= 1;
+ d >>= 1;
+ } while (d);
+
+ *n = res;
+ return rem;
+}
+EXPORT_SYMBOL(__div64_32);
+#endif
+
+/**
+ * div_s64_rem - signed 64bit divide with 64bit divisor and remainder
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ * @remainder: 64bit remainder
+ */
+#ifndef div_s64_rem
+s64 div_s64_rem(s64 dividend, s32 divisor, s32 *remainder)
+{
+ u64 quotient;
+
+ if (dividend < 0) {
+ quotient = div_u64_rem(-dividend, abs(divisor), (u32 *)remainder);
+ *remainder = -*remainder;
+ if (divisor > 0)
+ quotient = -quotient;
+ } else {
+ quotient = div_u64_rem(dividend, abs(divisor), (u32 *)remainder);
+ if (divisor < 0)
+ quotient = -quotient;
+ }
+ return quotient;
+}
+EXPORT_SYMBOL(div_s64_rem);
+#endif
+
+/**
+ * div64_u64_rem - unsigned 64bit divide with 64bit divisor and remainder
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ * @remainder: 64bit remainder
+ *
+ * This implementation is a comparable to algorithm used by div64_u64.
+ * But this operation, which includes math for calculating the remainder,
+ * is kept distinct to avoid slowing down the div64_u64 operation on 32bit
+ * systems.
+ */
+#ifndef div64_u64_rem
+u64 div64_u64_rem(u64 dividend, u64 divisor, u64 *remainder)
+{
+ u32 high = divisor >> 32;
+ u64 quot;
+
+ if (high == 0) {
+ u32 rem32;
+ quot = div_u64_rem(dividend, divisor, &rem32);
+ *remainder = rem32;
+ } else {
+ int n = fls(high);
+ quot = div_u64(dividend >> n, divisor >> n);
+
+ if (quot != 0)
+ quot--;
+
+ *remainder = dividend - quot * divisor;
+ if (*remainder >= divisor) {
+ quot++;
+ *remainder -= divisor;
+ }
+ }
+
+ return quot;
+}
+EXPORT_SYMBOL(div64_u64_rem);
+#endif
+
+/**
+ * div64_u64 - unsigned 64bit divide with 64bit divisor
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ *
+ * This implementation is a modified version of the algorithm proposed
+ * by the book 'Hacker's Delight'. The original source and full proof
+ * can be found here and is available for use without restriction.
+ *
+ * 'http://www.hackersdelight.org/hdcodetxt/divDouble.c.txt'
+ */
+#ifndef div64_u64
+u64 div64_u64(u64 dividend, u64 divisor)
+{
+ u32 high = divisor >> 32;
+ u64 quot;
+
+ if (high == 0) {
+ quot = div_u64(dividend, divisor);
+ } else {
+ int n = fls(high);
+ quot = div_u64(dividend >> n, divisor >> n);
+
+ if (quot != 0)
+ quot--;
+ if ((dividend - quot * divisor) >= divisor)
+ quot++;
+ }
+
+ return quot;
+}
+EXPORT_SYMBOL(div64_u64);
+#endif
+
+/**
+ * div64_s64 - signed 64bit divide with 64bit divisor
+ * @dividend: 64bit dividend
+ * @divisor: 64bit divisor
+ */
+#ifndef div64_s64
+s64 div64_s64(s64 dividend, s64 divisor)
+{
+ s64 quot, t;
+
+ quot = div64_u64(abs(dividend), abs(divisor));
+ t = (dividend ^ divisor) >> 63;
+
+ return (quot ^ t) - t;
+}
+EXPORT_SYMBOL(div64_s64);
+#endif
+
+#endif /* BITS_PER_LONG == 32 */
+
+/*
+ * Iterative div/mod for use when dividend is not expected to be much
+ * bigger than divisor.
+ */
+u32 iter_div_u64_rem(u64 dividend, u32 divisor, u64 *remainder)
+{
+ return __iter_div_u64_rem(dividend, divisor, remainder);
+}
+EXPORT_SYMBOL(iter_div_u64_rem);
+
+#ifndef mul_u64_u64_div_u64
+u64 mul_u64_u64_div_u64(u64 a, u64 b, u64 c)
+{
+ u64 res = 0, div, rem;
+ int shift;
+
+ /* can a * b overflow ? */
+ if (ilog2(a) + ilog2(b) > 62) {
+ /*
+ * (b * a) / c is equal to
+ *
+ * (b / c) * a +
+ * (b % c) * a / c
+ *
+ * if nothing overflows. Can the 1st multiplication
+ * overflow? Yes, but we do not care: this can only
+ * happen if the end result can't fit in u64 anyway.
+ *
+ * So the code below does
+ *
+ * res = (b / c) * a;
+ * b = b % c;
+ */
+ div = div64_u64_rem(b, c, &rem);
+ res = div * a;
+ b = rem;
+
+ shift = ilog2(a) + ilog2(b) - 62;
+ if (shift > 0) {
+ /* drop precision */
+ b >>= shift;
+ c >>= shift;
+ if (!c)
+ return res;
+ }
+ }
+
+ return res + div64_u64(a * b, c);
+}
+#endif
diff --git a/lib/math/rational.c b/lib/math/rational.c
new file mode 100644
index 0000000000..8411e912fb
--- /dev/null
+++ b/lib/math/rational.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * rational fractions
+ *
+ * Copyright (C) 2009 emlix GmbH, Oskar Schirmer <oskar@scara.com>
+ * Copyright (C) 2019 Trent Piepho <tpiepho@gmail.com>
+ *
+ * helper functions when coping with rational numbers
+ */
+
+#include <linux/rational.h>
+#include <linux/compiler.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+
+/*
+ * calculate best rational approximation for a given fraction
+ * taking into account restricted register size, e.g. to find
+ * appropriate values for a pll with 5 bit denominator and
+ * 8 bit numerator register fields, trying to set up with a
+ * frequency ratio of 3.1415, one would say:
+ *
+ * rational_best_approximation(31415, 10000,
+ * (1 << 8) - 1, (1 << 5) - 1, &n, &d);
+ *
+ * you may look at given_numerator as a fixed point number,
+ * with the fractional part size described in given_denominator.
+ *
+ * for theoretical background, see:
+ * https://en.wikipedia.org/wiki/Continued_fraction
+ */
+
+void rational_best_approximation(
+ unsigned long given_numerator, unsigned long given_denominator,
+ unsigned long max_numerator, unsigned long max_denominator,
+ unsigned long *best_numerator, unsigned long *best_denominator)
+{
+ /* n/d is the starting rational, which is continually
+ * decreased each iteration using the Euclidean algorithm.
+ *
+ * dp is the value of d from the prior iteration.
+ *
+ * n2/d2, n1/d1, and n0/d0 are our successively more accurate
+ * approximations of the rational. They are, respectively,
+ * the current, previous, and two prior iterations of it.
+ *
+ * a is current term of the continued fraction.
+ */
+ unsigned long n, d, n0, d0, n1, d1, n2, d2;
+ n = given_numerator;
+ d = given_denominator;
+ n0 = d1 = 0;
+ n1 = d0 = 1;
+
+ for (;;) {
+ unsigned long dp, a;
+
+ if (d == 0)
+ break;
+ /* Find next term in continued fraction, 'a', via
+ * Euclidean algorithm.
+ */
+ dp = d;
+ a = n / d;
+ d = n % d;
+ n = dp;
+
+ /* Calculate the current rational approximation (aka
+ * convergent), n2/d2, using the term just found and
+ * the two prior approximations.
+ */
+ n2 = n0 + a * n1;
+ d2 = d0 + a * d1;
+
+ /* If the current convergent exceeds the maxes, then
+ * return either the previous convergent or the
+ * largest semi-convergent, the final term of which is
+ * found below as 't'.
+ */
+ if ((n2 > max_numerator) || (d2 > max_denominator)) {
+ unsigned long t = ULONG_MAX;
+
+ if (d1)
+ t = (max_denominator - d0) / d1;
+ if (n1)
+ t = min(t, (max_numerator - n0) / n1);
+
+ /* This tests if the semi-convergent is closer than the previous
+ * convergent. If d1 is zero there is no previous convergent as this
+ * is the 1st iteration, so always choose the semi-convergent.
+ */
+ if (!d1 || 2u * t > a || (2u * t == a && d0 * dp > d1 * d)) {
+ n1 = n0 + t * n1;
+ d1 = d0 + t * d1;
+ }
+ break;
+ }
+ n0 = n1;
+ n1 = n2;
+ d0 = d1;
+ d1 = d2;
+ }
+ *best_numerator = n1;
+ *best_denominator = d1;
+}
diff --git a/lib/misc.c b/lib/misc.c
index 532f0bfc1e..1cb2a6b9b5 100644
--- a/lib/misc.c
+++ b/lib/misc.c
@@ -159,6 +159,8 @@ int mem_parse_options(int argc, char *argv[], char *optstr, int *mode,
*destfile = optarg;
break;
case 'x':
+ if (!swab)
+ return -EINVAL;
*swab = 1;
break;
default:
@@ -207,11 +209,14 @@ int memcpy_parse_options(int argc, char *argv[], int *sourcefd,
destfile = destfile ?: "/dev/mem";
*sourcefd = open_and_lseek(sourcefile, mode | O_RDONLY, src);
- if (*sourcefd < 0)
+ if (*sourcefd < 0) {
+ printf("Could not open source file \"%s\": %m\n", sourcefile);
return -1;
+ }
*destfd = open_and_lseek(destfile, mode | destmode, dest);
if (*destfd < 0) {
+ printf("Could not open destination file \"%s\": %m\n", destfile);
close(*sourcefd);
return -1;
}
diff --git a/lib/nls_base.c b/lib/nls_base.c
index cd6a5ff124..5def1411f9 100644
--- a/lib/nls_base.c
+++ b/lib/nls_base.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/*
* linux/fs/nls/nls_base.c
*
diff --git a/lib/notifier.c b/lib/notifier.c
index 9eb734e504..c55ec22f94 100644
--- a/lib/notifier.c
+++ b/lib/notifier.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <linux/list.h>
#include <notifier.h>
diff --git a/lib/parameter.c b/lib/parameter.c
index f57b7d07fd..c587d10eab 100644
--- a/lib/parameter.c
+++ b/lib/parameter.c
@@ -27,6 +27,8 @@
#include <string.h>
#include <globalvar.h>
#include <linux/err.h>
+#include <file-list.h>
+#include <stringlist.h>
static const char *param_type_string[] = {
[PARAM_TYPE_STRING] = "string",
@@ -39,6 +41,7 @@ static const char *param_type_string[] = {
[PARAM_TYPE_BITMASK] = "bitmask",
[PARAM_TYPE_IPV4] = "ipv4",
[PARAM_TYPE_MAC] = "MAC",
+ [PARAM_TYPE_FILE_LIST] = "file-list",
};
const char *get_param_type(struct param_d *param)
@@ -46,7 +49,7 @@ const char *get_param_type(struct param_d *param)
return param_type_string[param->type];
}
-struct param_d *get_param_by_name(struct device_d *dev, const char *name)
+struct param_d *get_param_by_name(struct device *dev, const char *name)
{
struct param_d *p;
@@ -64,7 +67,7 @@ struct param_d *get_param_by_name(struct device_d *dev, const char *name)
* @param name The name of the parameter
* @return The value
*/
-const char *dev_get_param(struct device_d *dev, const char *name)
+const char *dev_get_param(struct device *dev, const char *name)
{
struct param_d *param = get_param_by_name(dev, name);
@@ -82,33 +85,27 @@ const char *dev_get_param(struct device_d *dev, const char *name)
* @param name The name of the parameter
* @param val The new value of the parameter
*/
-int dev_set_param(struct device_d *dev, const char *name, const char *val)
+int dev_set_param(struct device *dev, const char *name, const char *val)
{
struct param_d *param;
int ret;
- if (!dev) {
- errno = ENODEV;
- return -ENODEV;
- }
+ if (!dev)
+ return errno_set(-ENODEV);
param = get_param_by_name(dev, name);
- if (!param) {
- errno = EINVAL;
- return -EINVAL;
- }
+ if (!param)
+ return errno_set(-EINVAL);
- if (param->flags & PARAM_FLAG_RO) {
- errno = EACCES;
- return -EACCES;
- }
+ if (param->flags & PARAM_FLAG_RO)
+ return errno_set(-EACCES);
ret = param->set(dev, param, val);
if (ret)
- errno = -ret;
+ return errno_set(ret);
- return ret;
+ return 0;
}
/**
@@ -122,8 +119,8 @@ int dev_set_param(struct device_d *dev, const char *name, const char *val)
* used during deregistration of the parameter to free the alloctated
* memory.
*/
-int dev_param_set_generic(struct device_d *dev, struct param_d *p,
- const char *val)
+int dev_param_set_generic(struct device *dev, struct param_d *p,
+ const char *val)
{
free(p->value);
if (!val) {
@@ -134,7 +131,7 @@ int dev_param_set_generic(struct device_d *dev, struct param_d *p,
return p->value ? 0 : -ENOMEM;
}
-static const char *param_get_generic(struct device_d *dev, struct param_d *p)
+static const char *param_get_generic(struct device *dev, struct param_d *p)
{
return p->value ? p->value : "";
}
@@ -147,10 +144,11 @@ static int compare(struct list_head *a, struct list_head *b)
return strcmp(na, nb);
}
-static int __dev_add_param(struct param_d *param, struct device_d *dev, const char *name,
- int (*set)(struct device_d *dev, struct param_d *p, const char *val),
- const char *(*get)(struct device_d *dev, struct param_d *p),
- unsigned long flags)
+static int __dev_add_param(struct param_d *param, struct device *dev,
+ const char *name,
+ int (*set)(struct device *dev, struct param_d *p, const char *val),
+ const char *(*get)(struct device *dev, struct param_d *p),
+ unsigned long flags)
{
if (get_param_by_name(dev, name))
return -EEXIST;
@@ -190,10 +188,10 @@ static int __dev_add_param(struct param_d *param, struct device_d *dev, const ch
* expect the parameter value to be a string which can be freed with free(). Do
* not use static arrays when using the generic functions.
*/
-struct param_d *dev_add_param(struct device_d *dev, const char *name,
- int (*set)(struct device_d *dev, struct param_d *p, const char *val),
- const char *(*get)(struct device_d *dev, struct param_d *param),
- unsigned long flags)
+struct param_d *dev_add_param(struct device *dev, const char *name,
+ int (*set)(struct device *dev, struct param_d *p, const char *val),
+ const char *(*get)(struct device *dev, struct param_d *param),
+ unsigned long flags)
{
struct param_d *param;
int ret;
@@ -215,7 +213,8 @@ struct param_d *dev_add_param(struct device_d *dev, const char *name,
* @param name The name of the parameter
* @param value The value of the parameter
*/
-struct param_d *dev_add_param_fixed(struct device_d *dev, const char *name, const char *value)
+struct param_d *dev_add_param_fixed(struct device *dev, const char *name,
+ const char *value)
{
struct param_d *param;
int ret;
@@ -245,7 +244,8 @@ static inline struct param_string *to_param_string(struct param_d *p)
return container_of(p, struct param_string, param);
}
-static int param_string_set(struct device_d *dev, struct param_d *p, const char *val)
+static int param_string_set(struct device *dev, struct param_d *p,
+ const char *val)
{
struct param_string *ps = to_param_string(p);
int ret;
@@ -255,12 +255,14 @@ static int param_string_set(struct device_d *dev, struct param_d *p, const char
if (!val)
val = "";
- value_new = xstrdup(val);
- value_new = strim(value_new);
+ value_new = xstrdup(skip_spaces(val));
+ strim(value_new);
*ps->value = value_new;
- if (!ps->set)
+ if (!ps->set) {
+ free(value_save);
return 0;
+ }
ret = ps->set(p, p->driver_priv);
if (ret) {
@@ -273,7 +275,7 @@ static int param_string_set(struct device_d *dev, struct param_d *p, const char
return ret;
}
-static const char *param_string_get(struct device_d *dev, struct param_d *p)
+static const char *param_string_get(struct device *dev, struct param_d *p)
{
struct param_string *ps = to_param_string(p);
int ret;
@@ -287,10 +289,10 @@ static const char *param_string_get(struct device_d *dev, struct param_d *p)
return *ps->value;
}
-struct param_d *dev_add_param_string(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- char **value, void *priv)
+struct param_d *dev_add_param_string(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ char **value, void *priv)
{
struct param_string *ps;
struct param_d *p;
@@ -327,7 +329,8 @@ static inline struct param_int *to_param_int(struct param_d *p)
return container_of(p, struct param_int, param);
}
-static int param_int_set(struct device_d *dev, struct param_d *p, const char *val)
+static int param_int_set(struct device *dev, struct param_d *p,
+ const char *val)
{
struct param_int *pi = to_param_int(p);
u8 value_save[pi->dsize];
@@ -368,7 +371,7 @@ static int param_int_set(struct device_d *dev, struct param_d *p, const char *va
return ret;
}
-static const char *param_int_get(struct device_d *dev, struct param_d *p)
+static const char *param_int_get(struct device *dev, struct param_d *p)
{
struct param_int *pi = to_param_int(p);
int ret;
@@ -418,10 +421,11 @@ int param_set_readonly(struct param_d *p, void *priv)
* The set function can be used as a notifer when the variable is about
* to be written. Can also be used to limit the value.
*/
-struct param_d *__dev_add_param_int(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- void *value, enum param_type type, const char *format, void *priv)
+struct param_d *__dev_add_param_int(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ void *value, enum param_type type,
+ const char *format, void *priv)
{
struct param_int *pi;
struct param_d *p;
@@ -487,7 +491,8 @@ static inline struct param_enum *to_param_enum(struct param_d *p)
return container_of(p, struct param_enum, param);
}
-static int param_enum_set(struct device_d *dev, struct param_d *p, const char *val)
+static int param_enum_set(struct device *dev, struct param_d *p,
+ const char *val)
{
struct param_enum *pe = to_param_enum(p);
int value_save = *pe->value;
@@ -515,7 +520,7 @@ static int param_enum_set(struct device_d *dev, struct param_d *p, const char *v
return ret;
}
-static const char *param_enum_get(struct device_d *dev, struct param_d *p)
+static const char *param_enum_get(struct device *dev, struct param_d *p)
{
struct param_enum *pe = to_param_enum(p);
int ret;
@@ -554,10 +559,11 @@ static void param_enum_info(struct param_d *p)
}
}
-struct param_d *dev_add_param_enum(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- int *value, const char * const *names, int num_names, void *priv)
+struct param_d *dev_add_param_enum(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ int *value, const char * const *names,
+ int num_names, void *priv)
{
struct param_enum *pe;
struct param_d *p;
@@ -591,17 +597,18 @@ static const char *const tristate_names[] = {
[PARAM_TRISTATE_FALSE] = "0",
};
-struct param_d *dev_add_param_tristate(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- int *value, void *priv)
+struct param_d *dev_add_param_tristate(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ int *value, void *priv)
{
return dev_add_param_enum(dev, name, set, get, value, tristate_names,
ARRAY_SIZE(tristate_names), priv);
}
-struct param_d *dev_add_param_tristate_ro(struct device_d *dev, const char *name,
- int *value)
+struct param_d *dev_add_param_tristate_ro(struct device *dev,
+ const char *name,
+ int *value)
{
return dev_add_param_enum_ro(dev, name, value, tristate_names,
ARRAY_SIZE(tristate_names));
@@ -621,7 +628,8 @@ static inline struct param_bitmask *to_param_bitmask(struct param_d *p)
return container_of(p, struct param_bitmask, param);
}
-static int param_bitmask_set(struct device_d *dev, struct param_d *p, const char *val)
+static int param_bitmask_set(struct device *dev, struct param_d *p,
+ const char *val)
{
struct param_bitmask *pb = to_param_bitmask(p);
void *value_save;
@@ -667,7 +675,7 @@ out:
return ret;
}
-static const char *param_bitmask_get(struct device_d *dev, struct param_d *p)
+static const char *param_bitmask_get(struct device *dev, struct param_d *p)
{
struct param_bitmask *pb = to_param_bitmask(p);
int ret, bit;
@@ -706,10 +714,12 @@ static void param_bitmask_info(struct param_d *p)
}
}
-struct param_d *dev_add_param_bitmask(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- unsigned long *value, const char * const *names, int max, void *priv)
+struct param_d *dev_add_param_bitmask(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ unsigned long *value,
+ const char * const *names, int max,
+ void *priv)
{
struct param_bitmask *pb;
struct param_d *p;
@@ -746,7 +756,6 @@ struct param_d *dev_add_param_bitmask(struct device_d *dev, const char *name,
struct param_ip {
struct param_d param;
IPaddr_t *ip;
- const char *format;
int (*set)(struct param_d *p, void *priv);
int (*get)(struct param_d *p, void *priv);
};
@@ -756,7 +765,8 @@ static inline struct param_ip *to_param_ip(struct param_d *p)
return container_of(p, struct param_ip, param);
}
-static int param_ip_set(struct device_d *dev, struct param_d *p, const char *val)
+static int param_ip_set(struct device *dev, struct param_d *p,
+ const char *val)
{
struct param_ip *pi = to_param_ip(p);
IPaddr_t ip_save = *pi->ip;
@@ -779,7 +789,7 @@ static int param_ip_set(struct device_d *dev, struct param_d *p, const char *val
return ret;
}
-static const char *param_ip_get(struct device_d *dev, struct param_d *p)
+static const char *param_ip_get(struct device *dev, struct param_d *p)
{
struct param_ip *pi = to_param_ip(p);
int ret;
@@ -796,10 +806,10 @@ static const char *param_ip_get(struct device_d *dev, struct param_d *p)
return p->value;
}
-struct param_d *dev_add_param_ip(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- IPaddr_t *ip, void *priv)
+struct param_d *dev_add_param_ip(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ IPaddr_t *ip, void *priv)
{
struct param_ip *pi;
int ret;
@@ -825,20 +835,19 @@ struct param_mac {
struct param_d param;
char *mac;
u8 mac_str[sizeof("xx:xx:xx:xx:xx:xx")];
- const char *format;
int (*set)(struct param_d *p, void *priv);
int (*get)(struct param_d *p, void *priv);
};
int string_to_ethaddr(const char *str, u8 enetaddr[6]);
-void ethaddr_to_string(const u8 enetaddr[6], char *str);
static inline struct param_mac *to_param_mac(struct param_d *p)
{
return container_of(p, struct param_mac, param);
}
-static int param_mac_set(struct device_d *dev, struct param_d *p, const char *val)
+static int param_mac_set(struct device *dev, struct param_d *p,
+ const char *val)
{
struct param_mac *pm = to_param_mac(p);
char mac_save[6];
@@ -867,7 +876,7 @@ out:
return ret;
}
-static const char *param_mac_get(struct device_d *dev, struct param_d *p)
+static const char *param_mac_get(struct device *dev, struct param_d *p)
{
struct param_mac *pm = to_param_mac(p);
int ret;
@@ -878,15 +887,15 @@ static const char *param_mac_get(struct device_d *dev, struct param_d *p)
return NULL;
}
- ethaddr_to_string(pm->mac, p->value);
+ sprintf(p->value, "%pM", pm->mac);
return p->value;
}
-struct param_d *dev_add_param_mac(struct device_d *dev, const char *name,
- int (*set)(struct param_d *p, void *priv),
- int (*get)(struct param_d *p, void *priv),
- u8 *mac, void *priv)
+struct param_d *dev_add_param_mac(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ u8 *mac, void *priv)
{
struct param_mac *pm;
int ret;
@@ -909,6 +918,93 @@ struct param_d *dev_add_param_mac(struct device_d *dev, const char *name,
return &pm->param;
}
+struct param_file_list {
+ struct param_d param;
+ struct file_list **file_list;
+ char *file_list_str;
+ int (*set)(struct param_d *p, void *priv);
+ int (*get)(struct param_d *p, void *priv);
+};
+
+static inline struct param_file_list *to_param_file_list(struct param_d *p)
+{
+ return container_of(p, struct param_file_list, param);
+}
+
+static int param_file_list_set(struct device *dev, struct param_d *p,
+ const char *val)
+{
+ struct param_file_list *pfl = to_param_file_list(p);
+ struct file_list *file_list_save = *pfl->file_list;
+ int ret;
+
+ if (!val)
+ val = "";
+
+ *pfl->file_list = file_list_parse(val);
+ if (IS_ERR(*pfl->file_list)) {
+ ret = PTR_ERR(*pfl->file_list);
+ goto out;
+ }
+
+ if (pfl->set) {
+ ret = pfl->set(p, p->driver_priv);
+ if (ret) {
+ file_list_free(*pfl->file_list);
+ goto out;
+ }
+ }
+
+ return 0;
+out:
+ *pfl->file_list = file_list_save;
+
+ return ret;
+}
+
+static const char *param_file_list_get(struct device *dev, struct param_d *p)
+{
+ struct param_file_list *pfl = to_param_file_list(p);
+ int ret;
+
+ if (pfl->get) {
+ ret = pfl->get(p, p->driver_priv);
+ if (ret)
+ return NULL;
+ }
+
+ free(p->value);
+ p->value = file_list_to_str(*pfl->file_list);
+ return p->value;
+}
+
+struct param_d *dev_add_param_file_list(struct device *dev, const char *name,
+ int (*set)(struct param_d *p, void *priv),
+ int (*get)(struct param_d *p, void *priv),
+ struct file_list **file_list,
+ void *priv)
+{
+ struct param_file_list *pfl;
+ int ret;
+
+ pfl = xzalloc(sizeof(*pfl));
+ pfl->file_list = file_list;
+ pfl->set = set;
+ pfl->get = get;
+ pfl->param.driver_priv = priv;
+ pfl->param.type = PARAM_TYPE_FILE_LIST;
+
+ ret = __dev_add_param(&pfl->param, dev, name,
+ param_file_list_set, param_file_list_get, 0);
+ if (ret) {
+ free(pfl);
+ return ERR_PTR(ret);
+ }
+
+ return &pfl->param;
+}
+
+
/**
* dev_remove_param - remove a parameter from a device and free its
* memory
@@ -927,7 +1023,7 @@ void dev_remove_param(struct param_d *p)
* memory
* @param dev The device
*/
-void dev_remove_parameters(struct device_d *dev)
+void dev_remove_parameters(struct device *dev)
{
struct param_d *p, *n;
diff --git a/lib/parseopt.c b/lib/parseopt.c
index 70983066d9..4fdd72fa15 100644
--- a/lib/parseopt.c
+++ b/lib/parseopt.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include "parseopt.h"
@@ -59,37 +61,6 @@ again:
*val = v;
}
-void parseopt_u16(const char *options, const char *opt, uint16_t *val)
-{
- const char *start;
- size_t optlen = strlen(opt);
- ulong v;
- char *endp;
-
-again:
- start = strstr(options, opt);
-
- if (!start)
- return;
-
- if (start > options && start[-1] != ',') {
- options = start;
- goto again;
- }
-
- if (start[optlen] != '=') {
- options = start;
- goto again;
- }
-
- v = simple_strtoul(start + optlen + 1, &endp, 0);
- if (v > U16_MAX)
- return;
-
- if (*endp == ',' || *endp == '\0')
- *val = v;
-}
-
void parseopt_str(const char *options, const char *opt, char **val)
{
const char *start;
diff --git a/lib/random.c b/lib/random.c
index 759271f0c8..e83935d0e1 100644
--- a/lib/random.c
+++ b/lib/random.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <stdlib.h>
#include <linux/hw_random.h>
@@ -32,17 +34,8 @@ void get_random_bytes(void *_buf, int len)
*buf++ = rand() % 256;
}
-/**
- * get_crypto_bytes - get random numbers suitable for cryptographic needs.
- */
-static int _get_crypto_bytes(void *buf, int len)
+int hwrng_get_crypto_bytes(struct hwrng *rng, void *buf, int len)
{
- struct hwrng *rng;
-
- rng = hwrng_get_first();
- if (IS_ERR(rng))
- return PTR_ERR(rng);
-
while (len) {
int bytes = hwrng_get_data(rng, buf, len, true);
if (!bytes)
@@ -58,20 +51,28 @@ static int _get_crypto_bytes(void *buf, int len)
return 0;
}
+/**
+ * get_crypto_bytes - get random numbers suitable for cryptographic needs.
+ */
int get_crypto_bytes(void *buf, int len)
{
+ struct hwrng *rng;
int err;
- err = _get_crypto_bytes(buf, len);
- if (!err)
- return 0;
+ rng = hwrng_get_first();
+ err = PTR_ERR_OR_ZERO(rng);
+ if (!err) {
+ err = hwrng_get_crypto_bytes(rng, buf, len);
+ if (!err)
+ return 0;
+ }
if (!IS_ENABLED(CONFIG_ALLOW_PRNG_FALLBACK)) {
- pr_err("error: no HWRNG available!\n");
+ pr_err("no HWRNG available!\n");
return err;
}
- pr_warn("warning: falling back to Pseudo RNG source!\n");
+ pr_warn("falling back to Pseudo RNG source!\n");
get_random_bytes(buf, len);
diff --git a/lib/ratp.c b/lib/ratp.c
index 8167c5bd4c..c597e96784 100644
--- a/lib/ratp.c
+++ b/lib/ratp.c
@@ -738,7 +738,7 @@ static int ratp_behaviour_c2(struct ratp_internal *ri, void *pkt)
control = RATP_CONTROL_RST | RATP_CONTROL_ACK |
ratp_set_sn(ratp_an(hdr)) | ratp_set_next_an(ratp_sn(hdr));
ratp_send_hdr(ri, control);
-
+ ri->sendbuf_len = 0;
ratp_state_change(ri, RATP_STATE_CLOSED);
return 1;
}
@@ -784,7 +784,7 @@ static int ratp_behaviour_d1(struct ratp_internal *ri, void *pkt)
ri->status = -ECONNREFUSED;
pr_debug("Error: connection refused\n");
-
+ ri->sendbuf_len = 0;
ratp_state_change(ri, RATP_STATE_CLOSED);
return 1;
@@ -812,6 +812,8 @@ static int ratp_behaviour_d2(struct ratp_internal *ri, void *pkt)
ri->status = -ECONNRESET;
pr_debug("connection reset\n");
+ ri->sendbuf_len = 0;
+ ratp_state_change(ri, RATP_STATE_CLOSED);
return 0;
}
@@ -879,7 +881,7 @@ static int ratp_behaviour_e(struct ratp_internal *ri, void *pkt)
ratp_send_hdr(ri, control);
pr_debug("connection reset\n");
-
+ ri->sendbuf_len = 0;
ratp_state_change(ri, RATP_STATE_CLOSED);
return 1;
@@ -924,8 +926,10 @@ static int ratp_behaviour_f1(struct ratp_internal *ri, void *pkt)
if (!(hdr->control & RATP_CONTROL_ACK))
return 1;
- if (ratp_an_expected(ri, hdr))
+ if (ratp_an_expected(ri, hdr)) {
+ ri->sendbuf_len = 0; /* packet succesfully received */
return 0;
+ }
control = RATP_CONTROL_RST | ratp_set_sn(ratp_an(hdr));
ratp_send_hdr(ri, control);
@@ -971,6 +975,7 @@ static int ratp_behaviour_f2(struct ratp_internal *ri, void *pkt)
if (ri->sendmsg_current)
ratp_msg_done(ri, ri->sendmsg_current, 0);
ri->sendmsg_current = NULL;
+ ri->sendbuf_len = 0; /* packet succesfully received */
return 0;
} else {
pr_vdebug("%s: an not expected\n", __func__);
@@ -1109,6 +1114,8 @@ static int ratp_behaviour_h2(struct ratp_internal *ri, void *pkt)
ratp_state_change(ri, RATP_STATE_LAST_ACK);
+ ri->sn_received = ratp_sn(hdr);
+
return 1;
}
@@ -1173,6 +1180,7 @@ static int ratp_behaviour_h3(struct ratp_internal *ri, void *pkt)
ratp_send_hdr(ri, control);
ri->status = -ECONNRESET;
pr_debug("Error: Connection reset\n");
+ ri->sendbuf_len = 0;
ratp_state_change(ri, RATP_STATE_CLOSED);
return 1;
}
@@ -1215,8 +1223,10 @@ static int ratp_behaviour_h4(struct ratp_internal *ri, void *pkt)
pr_debug("%s\n", __func__);
- if (ratp_an_expected(ri, hdr))
+ if (ratp_an_expected(ri, hdr)) {
+ ri->sendbuf_len = 0; /* packet succesfully received */
ratp_state_change(ri, RATP_STATE_CLOSED);
+ }
return 1;
}
@@ -1242,6 +1252,7 @@ static int ratp_behaviour_h5(struct ratp_internal *ri, void *pkt)
pr_debug("%s\n", __func__);
if (ratp_an_expected(ri, hdr)) {
+ ri->sendbuf_len = 0; /* packet succesfully received */
ratp_state_change(ri, RATP_STATE_TIME_WAIT);
ratp_start_time_wait_timer(ri);
}
@@ -1578,9 +1589,8 @@ int ratp_poll(struct ratp *ratp)
}
}
- if (ri->sendmsg_current && is_timeout(ri->retransmission_timer_start,
+ if (ri->sendbuf_len && is_timeout(ri->retransmission_timer_start,
ri->rto * MSECOND)) {
-
ri->retransmission_count++;
if (ri->retransmission_count == ri->max_retransmission) {
ri->status = ret = -ETIMEDOUT;
@@ -1599,7 +1609,7 @@ int ratp_poll(struct ratp *ratp)
goto out;
}
- if (!ri->sendmsg_current && !list_empty(&ri->sendmsg))
+ if (ri->sendbuf_len == 0 && !list_empty(&ri->sendmsg))
ratp_send_next_data(ri);
ret = 0;
@@ -1638,7 +1648,7 @@ int ratp_establish(struct ratp *ratp, bool active, int timeout_ms)
INIT_LIST_HEAD(&ri->sendmsg);
ri->max_retransmission = 100;
ri->srtt = 100;
- ri->rto = 100;
+ ri->rto = 200;
ri->active = active;
ri->in_ratp++;
@@ -1714,6 +1724,8 @@ void ratp_close(struct ratp *ratp)
list_for_each_entry_safe(msg, tmp, &ri->sendmsg, list)
ratp_msg_done(ri, msg, -ECONNRESET);
+ free(ri->recvbuf);
+ free(ri->sendbuf);
free(ri);
ratp->internal = NULL;
diff --git a/lib/readkey.c b/lib/readkey.c
index fd72951046..c26e9d51ab 100644
--- a/lib/readkey.c
+++ b/lib/readkey.c
@@ -61,7 +61,7 @@ int read_key(void)
esc[i] = getchar();
if (esc[i++] == '~')
break;
- if (i == ARRAY_SIZE(esc))
+ if (i == ARRAY_SIZE(esc) - 1)
return -1;
}
}
diff --git a/lib/readline.c b/lib/readline.c
index 3d16c1838c..92bec3d1d8 100644
--- a/lib/readline.c
+++ b/lib/readline.c
@@ -1,9 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <readkey.h>
#include <init.h>
#include <libbb.h>
-#include <poller.h>
-#include <ratp_bb.h>
+#include <sched.h>
#include <xfuncs.h>
#include <complete.h>
#include <linux/ctype.h>
@@ -200,11 +201,8 @@ int readline(const char *prompt, char *buf, int len)
puts (prompt);
while (1) {
- while (!tstc()) {
- poller_call();
- if (IS_ENABLED(CONFIG_CONSOLE_RATP))
- barebox_ratp_command_run();
- }
+ while (!tstc())
+ resched();
ichar = read_key();
@@ -232,9 +230,14 @@ int readline(const char *prompt, char *buf, int len)
}
i = 0;
- while (completestr[i])
+ while (completestr[i]) {
+ if (completestr[i] == ' ' && completestr[i + 1])
+ cread_add_char('\\', insert, &num,
+ &eol_num, buf, len);
+
cread_add_char(completestr[i++], insert, &num,
&eol_num, buf, len);
+ }
#endif
break;
diff --git a/lib/readline_simple.c b/lib/readline_simple.c
index fcdbca41a9..7a8df95494 100644
--- a/lib/readline_simple.c
+++ b/lib/readline_simple.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
static char erase_seq[] = "\b \b"; /* erase sequence */
@@ -44,7 +46,7 @@ int readline (const char *prompt, char *line, int len)
int n = 0; /* buffer index */
int plen = 0; /* prompt length */
int col; /* output column cnt */
- char c;
+ int c;
/* print prompt */
if (prompt) {
diff --git a/lib/reed_solomon/Makefile b/lib/reed_solomon/Makefile
index c3d7136827..f7475981b9 100644
--- a/lib/reed_solomon/Makefile
+++ b/lib/reed_solomon/Makefile
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
#
# This is a modified version of reed solomon lib,
#
diff --git a/lib/reed_solomon/reed_solomon.c b/lib/reed_solomon/reed_solomon.c
index 51c67c3c8d..80c0ec4f7a 100644
--- a/lib/reed_solomon/reed_solomon.c
+++ b/lib/reed_solomon/reed_solomon.c
@@ -44,7 +44,7 @@
#include <module.h>
#include <linux/string.h>
#include <stdio.h>
-#include <asm-generic/errno.h>
+#include <linux/errno.h>
/* This list holds all currently allocated rs control structures */
static LIST_HEAD (rslist);
diff --git a/lib/refcount.c b/lib/refcount.c
new file mode 100644
index 0000000000..8c6f98b480
--- /dev/null
+++ b/lib/refcount.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Out-of-line refcount functions.
+ */
+
+#include <linux/refcount.h>
+#include <linux/printk.h>
+
+#define REFCOUNT_WARN(str) WARN_ONCE(1, "refcount_t: " str ".\n")
+
+void refcount_warn_saturate(refcount_t *r, enum refcount_saturation_type t)
+{
+ refcount_set(r, REFCOUNT_SATURATED);
+
+ switch (t) {
+ case REFCOUNT_ADD_NOT_ZERO_OVF:
+ REFCOUNT_WARN("saturated; leaking memory");
+ break;
+ case REFCOUNT_ADD_OVF:
+ REFCOUNT_WARN("saturated; leaking memory");
+ break;
+ case REFCOUNT_ADD_UAF:
+ REFCOUNT_WARN("addition on 0; use-after-free");
+ break;
+ case REFCOUNT_SUB_UAF:
+ REFCOUNT_WARN("underflow; use-after-free");
+ break;
+ case REFCOUNT_DEC_LEAK:
+ REFCOUNT_WARN("decrement hit 0; leaking memory");
+ break;
+ default:
+ REFCOUNT_WARN("unknown saturation event!?");
+ }
+}
diff --git a/lib/show_progress.c b/lib/show_progress.c
index 85085790f6..1b624bcb9a 100644
--- a/lib/show_progress.c
+++ b/lib/show_progress.c
@@ -17,15 +17,15 @@
#include <common.h>
#include <fs.h>
#include <progress.h>
-#include <asm-generic/div64.h>
+#include <linux/math64.h>
-#define HASHES_PER_LINE 65
+#define HASHES_PER_LINE 64
-static int printed;
-static int progress_max;
-static int spin;
+static loff_t printed;
+static loff_t progress_max;
+static unsigned spin;
-void show_progress(int now)
+void show_progress(loff_t now)
{
char spinchr[] = "\\|/-";
@@ -35,9 +35,8 @@ void show_progress(int now)
}
if (progress_max && progress_max != FILESIZE_MAX) {
- uint64_t tmp = (int64_t)now * HASHES_PER_LINE;
- do_div(tmp, progress_max);
- now = tmp;
+ uint64_t tmp = now * HASHES_PER_LINE;
+ now = div64_u64(tmp, progress_max);
}
while (printed < now) {
@@ -48,7 +47,7 @@ void show_progress(int now)
}
}
-void init_progression_bar(int max)
+void init_progression_bar(loff_t max)
{
printed = 0;
progress_max = max;
@@ -58,3 +57,31 @@ void init_progression_bar(int max)
else
printf("\t");
}
+
+NOTIFIER_HEAD(progress_notifier_list);
+
+static int progress_logger(struct notifier_block *r, unsigned long stage, void *_prefix)
+{
+ const char *prefix = _prefix;
+
+ switch ((enum progress_stage)stage) {
+ case PROGRESS_UPDATING:
+ pr_info("%sSoftware update in progress\n", prefix);
+ break;
+ case PROGRESS_UPDATE_SUCCESS:
+ pr_info("%sSoftware update finished successfully\n", prefix);
+ break;
+ case PROGRESS_UPDATE_FAIL:
+ pr_info("%sSoftware update failed\n", prefix);
+ break;
+ case PROGRESS_UNSPECIFIED:
+ /* default state. This should not be reached */
+ break;
+ }
+
+ return 0;
+}
+
+struct notifier_block progress_log_client = {
+ .notifier_call = progress_logger
+};
diff --git a/lib/stackprot.c b/lib/stackprot.c
new file mode 100644
index 0000000000..7a8d0a4c10
--- /dev/null
+++ b/lib/stackprot.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#define pr_fmt(fmt) "stackprot: " fmt
+
+#include <linux/printk.h>
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <init.h>
+#include <stdlib.h>
+
+#ifdef __PBL__
+#define STAGE "PBL"
+#else
+#define STAGE "barebox"
+#endif
+
+void __stack_chk_fail(void);
+
+volatile ulong __stack_chk_guard = (ulong)(0xfeedf00ddeadbeef & ~0UL);
+
+/*
+ * Called when gcc's -fstack-protector feature is used, and
+ * gcc detects corruption of the on-stack canary value
+ */
+noinstr void __stack_chk_fail(void)
+{
+ panic("stack-protector: " STAGE " stack is corrupted in: %pS\n", _RET_IP_);
+}
+EXPORT_SYMBOL(__stack_chk_fail);
+
+static __no_stack_protector int stackprot_randomize_guard(void)
+{
+ ulong chk_guard;
+ int ret;
+
+ ret = get_crypto_bytes(&chk_guard, sizeof(chk_guard));
+ if (ret)
+ pr_warn("proceeding without randomized stack protector\n");
+ else
+ __stack_chk_guard = chk_guard;
+
+ return 0;
+}
+late_initcall(stackprot_randomize_guard);
diff --git a/lib/string.c b/lib/string.c
index 717b59aa50..374f326143 100644
--- a/lib/string.c
+++ b/lib/string.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* linux/lib/string.c
*
@@ -13,23 +14,39 @@
* * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
* - Added strsep() which will replace strtok() soon (because strsep() is
* reentrant and should be faster). Use only strsep() in new code, please.
+ * * Mon Sep 14 2020, Ahmad Fatoum <a.fatoum@pengutronix.de>
+ * - Kissed strtok() goodbye
+ *
*/
#include <linux/types.h>
#include <string.h>
#include <linux/ctype.h>
+#include <asm/word-at-a-time.h>
#include <malloc.h>
-char * ___strtok;
+#ifndef __HAVE_ARCH_STRCASECMP
+int strcasecmp(const char *s1, const char *s2)
+{
+ int c1, c2;
-#ifndef __HAVE_ARCH_STRNICMP
+ do {
+ c1 = tolower(*s1++);
+ c2 = tolower(*s2++);
+ } while (c1 == c2 && c1 != 0);
+ return c1 - c2;
+}
+EXPORT_SYMBOL(strcasecmp);
+#endif
+
+#ifndef __HAVE_ARCH_STRNCASECMP
/**
- * strnicmp - Case insensitive, length-limited string comparison
+ * strncasecmp - Case insensitive, length-limited string comparison
* @s1: One string
* @s2: The other string
* @len: the maximum number of characters to compare
*/
-int strnicmp(const char *s1, const char *s2, size_t len)
+int strncasecmp(const char *s1, const char *s2, size_t len)
{
/* Yes, Virginia, it had better be unsigned */
unsigned char c1, c2;
@@ -51,53 +68,108 @@ int strnicmp(const char *s1, const char *s2, size_t len)
} while (--len);
return (int)c1 - (int)c2;
}
-EXPORT_SYMBOL(strnicmp);
+EXPORT_SYMBOL(strncasecmp);
#endif
-#ifndef __HAVE_ARCH_STRCASECMP
-int strcasecmp(const char *s1, const char *s2)
+#ifndef __HAVE_ARCH_STRCPY
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest,const char *src)
{
- int c1, c2;
+ char *tmp = dest;
- do {
- c1 = tolower(*s1++);
- c2 = tolower(*s2++);
- } while (c1 == c2 && c1 != 0);
- return c1 - c2;
+ while ((*dest++ = *src++) != '\0')
+ /* nothing */;
+ return tmp;
}
-EXPORT_SYMBOL(strcasecmp);
#endif
+EXPORT_SYMBOL(strcpy);
-#ifndef __HAVE_ARCH_STRNCASECMP
-int strncasecmp(const char *s1, const char *s2, size_t n)
+#ifndef __HAVE_ARCH_STRSCPY
+ssize_t strscpy(char *dest, const char *src, size_t count)
{
- int c1, c2;
+ const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
+ size_t max = count;
+ long res = 0;
+
+ if (count == 0 || WARN_ON_ONCE(count > INT_MAX))
+ return -E2BIG;
+
+#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
+ /*
+ * If src is unaligned, don't cross a page boundary,
+ * since we don't know if the next page is mapped.
+ */
+ if ((long)src & (sizeof(long) - 1)) {
+ size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1));
+ if (limit < max)
+ max = limit;
+ }
+#else
+ /* If src or dest is unaligned, don't do word-at-a-time. */
+ if (((long) dest | (long) src) & (sizeof(long) - 1))
+ max = 0;
+#endif
- do {
- c1 = tolower(*s1++);
- c2 = tolower(*s2++);
- } while ((--n > 0) && c1 == c2 && c1 != 0);
- return c1 - c2;
+ /*
+ * read_word_at_a_time() below may read uninitialized bytes after the
+ * trailing zero and use them in comparisons. Disable this optimization
+ * under KMSAN to prevent false positive reports.
+ */
+ if (IS_ENABLED(CONFIG_KMSAN))
+ max = 0;
+
+ while (max >= sizeof(unsigned long)) {
+ unsigned long c, data;
+
+ c = read_word_at_a_time(src+res);
+ if (has_zero(c, &data, &constants)) {
+ data = prep_zero_mask(c, data, &constants);
+ data = create_zero_mask(data);
+ *(unsigned long *)(dest+res) = c & zero_bytemask(data);
+ return res + find_zero(data);
+ }
+ *(unsigned long *)(dest+res) = c;
+ res += sizeof(unsigned long);
+ count -= sizeof(unsigned long);
+ max -= sizeof(unsigned long);
+ }
+
+ while (count) {
+ char c;
+
+ c = src[res];
+ dest[res] = c;
+ if (!c)
+ return res;
+ res++;
+ count--;
+ }
+
+ /* Hit buffer length without finding a NUL; force NUL-termination. */
+ if (res)
+ dest[res-1] = '\0';
+
+ return -E2BIG;
}
-EXPORT_SYMBOL(strncasecmp);
+EXPORT_SYMBOL(strscpy);
#endif
-#ifndef __HAVE_ARCH_STRCPY
/**
- * strcpy - Copy a %NUL terminated string
+ * stpcpy - Copy a %NUL terminated string, but return pointer to %NUL
* @dest: Where to copy the string to
* @src: Where to copy the string from
*/
-char * strcpy(char * dest,const char *src)
+char *stpcpy(char *dest, const char *src)
{
- char *tmp = dest;
-
while ((*dest++ = *src++) != '\0')
/* nothing */;
- return tmp;
+ return dest - 1;
}
-#endif
-EXPORT_SYMBOL(strcpy);
+EXPORT_SYMBOL(stpcpy);
#ifndef __HAVE_ARCH_STRNCPY
/**
@@ -208,6 +280,8 @@ int strcmp(const char * cs,const char * ct)
{
register signed char __res;
+ BUG_ON(!cs || !ct);
+
while (1) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
@@ -229,6 +303,8 @@ int strncmp(const char * cs, const char * ct, size_t count)
{
register signed char __res = 0;
+ BUG_ON(!cs || !ct);
+
while (count) {
if ((__res = *cs - *ct++) != 0 || !*cs++)
break;
@@ -256,6 +332,24 @@ char * _strchr(const char * s, int c)
#endif
EXPORT_SYMBOL(_strchr);
+#ifndef __HAVE_ARCH_STRCHRNUL
+/**
+ * strchrnul - Find and return a character in a string, or end of string
+ * @s: The string to be searched
+ * @c: The character to search for
+ *
+ * Returns pointer to first occurrence of 'c' in s. If c is not found, then
+ * return a pointer to the null byte at the end of s.
+ */
+char *strchrnul(const char *s, int c)
+{
+ while (*s && *s != (char)c)
+ s++;
+ return (char *)s;
+}
+EXPORT_SYMBOL(strchrnul);
+#endif
+
#ifndef __HAVE_ARCH_STRRCHR
/**
* strrchr - Find the last occurrence of a character in a string
@@ -392,36 +486,6 @@ char * strpbrk(const char * cs,const char * ct)
#endif
EXPORT_SYMBOL(strpbrk);
-#ifndef __HAVE_ARCH_STRTOK
-/**
- * strtok - Split a string into tokens
- * @s: The string to be searched
- * @ct: The characters to search for
- *
- * WARNING: strtok is deprecated, use strsep instead.
- */
-char * strtok(char * s, const char * ct)
-{
- char *sbegin, *send;
-
- sbegin = s ? s : ___strtok;
- if (!sbegin) {
- return NULL;
- }
- sbegin += strspn(sbegin,ct);
- if (*sbegin == '\0') {
- ___strtok = NULL;
- return( NULL );
- }
- send = strpbrk( sbegin, ct);
- if (send && *send != '\0')
- *send++ = '\0';
- ___strtok = send;
- return (sbegin);
-}
-#endif
-EXPORT_SYMBOL(strtok);
-
#ifndef __HAVE_ARCH_STRSEP
/**
* strsep - Split a string into tokens
@@ -451,6 +515,49 @@ char * strsep(char **s, const char *ct)
#endif
EXPORT_SYMBOL(strsep);
+/**
+ * strsep_unescaped - Split a string into tokens, while ignoring escaped delimiters
+ * @s: The string to be searched
+ * @ct: The delimiter characters to search for
+ *
+ * strsep_unescaped() behaves like strsep unless it meets an escaped delimiter.
+ * In that case, it shifts the string back in memory to overwrite the escape's
+ * backslash then continues the search until an unescaped delimiter is found.
+ */
+char *strsep_unescaped(char **s, const char *ct)
+{
+ char *sbegin = *s, *hay;
+ const char *needle;
+ size_t shift = 0;
+
+ if (sbegin == NULL)
+ return NULL;
+
+ for (hay = sbegin; *hay != '\0'; ++hay) {
+ *hay = hay[shift];
+
+ if (*hay == '\\') {
+ *hay = hay[++shift];
+ if (*hay != '\\')
+ continue;
+ }
+
+ for (needle = ct; *needle != '\0'; ++needle) {
+ if (*hay == *needle)
+ goto match;
+ }
+ }
+
+ *s = NULL;
+ return sbegin;
+
+match:
+ *hay = '\0';
+ *s = &hay[shift + 1];
+
+ return sbegin;
+}
+
#ifndef __HAVE_ARCH_STRSWAB
/**
* strswab - swap adjacent even and odd bytes in %NUL-terminated string
@@ -487,7 +594,7 @@ char *strswab(const char *s)
*
* Do not use memset() to access IO space, use memset_io() instead.
*/
-void *__default_memset(void * s,int c,size_t count)
+void *__default_memset(void * s, int c, size_t count)
{
char *xs = (char *) s;
@@ -498,8 +605,20 @@ void *__default_memset(void * s,int c,size_t count)
}
EXPORT_SYMBOL(__default_memset);
+void __prereloc __no_sanitize_address *__nokasan_default_memset(void * s, int c, size_t count)
+{
+ char *xs = (char *) s;
+
+ while (count--)
+ *xs++ = c;
+
+ return s;
+}
+EXPORT_SYMBOL(__nokasan_default_memset);
+
#ifndef __HAVE_ARCH_MEMSET
void *memset(void *s, int c, size_t count) __alias(__default_memset);
+void *__memset(void *s, int c, size_t count) __alias(__default_memset);
#endif
/**
@@ -511,7 +630,19 @@ void *memset(void *s, int c, size_t count) __alias(__default_memset);
* You should not use this function to access IO space, use memcpy_toio()
* or memcpy_fromio() instead.
*/
-void *__default_memcpy(void * dest,const void *src,size_t count)
+void *__default_memcpy(void * dest,const void *src, size_t count)
+{
+ char *tmp = (char *) dest, *s = (char *) src;
+
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+EXPORT_SYMBOL(__default_memcpy);
+
+void __no_sanitize_address *__nokasan_default_memcpy(void * dest,
+ const void *src, size_t count)
{
char *tmp = (char *) dest, *s = (char *) src;
@@ -520,13 +651,20 @@ void *__default_memcpy(void * dest,const void *src,size_t count)
return dest;
}
-EXPORT_SYMBOL(memcpy);
+EXPORT_SYMBOL(__nokasan_default_memcpy);
#ifndef __HAVE_ARCH_MEMCPY
void *memcpy(void * dest, const void *src, size_t count)
__alias(__default_memcpy);
+void *__memcpy(void * dest, const void *src, size_t count)
+ __alias(__default_memcpy);
#endif
+void *mempcpy(void *dest, const void *src, size_t count)
+{
+ return memcpy(dest, src, count) + count;
+}
+EXPORT_SYMBOL(mempcpy);
#ifndef __HAVE_ARCH_MEMMOVE
/**
@@ -808,3 +946,106 @@ int strtobool(const char *str, int *val)
return -EINVAL;
}
EXPORT_SYMBOL(strtobool);
+
+bool strends(const char *str, const char *postfix)
+{
+ if (strlen(str) < strlen(postfix))
+ return false;
+
+ return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
+}
+EXPORT_SYMBOL(strends);
+
+/**
+ * match_string - matches given string in an array
+ * @array: array of strings
+ * @n: number of strings in the array or -1 for NULL terminated arrays
+ * @string: string to match with
+ *
+ * This routine will look for a string in an array of strings up to the
+ * n-th element in the array or until the first NULL element.
+ *
+ * Historically the value of -1 for @n, was used to search in arrays that
+ * are NULL terminated. However, the function does not make a distinction
+ * when finishing the search: either @n elements have been compared OR
+ * the first NULL element was found.
+ *
+ * Return:
+ * index of a @string in the @array if matches, or %-EINVAL otherwise.
+ */
+int match_string(const char * const *array, size_t n, const char *string)
+{
+ int index;
+ const char *item;
+
+ for (index = 0; index < n; index++) {
+ item = array[index];
+ if (!item)
+ break;
+ if (!strcmp(item, string))
+ return index;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL(match_string);
+
+char *parse_assignment(char *str)
+{
+ char *value;
+
+ value = strchr(str, '=');
+ if (value)
+ *value++ = '\0';
+
+ return value;
+}
+
+char *strjoin(const char *separator, char **arr, size_t arrlen)
+{
+ size_t separatorlen;
+ int len = 1; /* '\0' */
+ char *buf, *p;
+ int i;
+
+ separatorlen = strlen(separator);
+
+ for (i = 0; i < arrlen; i++)
+ len += strlen(arr[i]) + separatorlen;
+
+ if (!arrlen)
+ return xzalloc(1);
+
+ p = buf = xmalloc(len);
+
+ for (i = 0; i < arrlen - 1; i++) {
+ p = stpcpy(p, arr[i]);
+ p = mempcpy(p, separator, separatorlen);
+ }
+
+ stpcpy(p, arr[i]);
+
+ return buf;
+}
+EXPORT_SYMBOL(strjoin);
+
+/**
+ * strreplace - Replace all occurrences of character in string.
+ * @str: The string to operate on.
+ * @old: The character being replaced.
+ * @new: The character @old is replaced with.
+ *
+ * Replaces the each @old character with a @new one in the given string @str.
+ *
+ * Return: pointer to the string @str itself.
+ */
+char *strreplace(char *str, char old, char new)
+{
+ char *s = str;
+
+ for (; *s; ++s)
+ if (*s == old)
+ *s = new;
+ return str;
+}
+EXPORT_SYMBOL(strreplace);
diff --git a/lib/stringlist.c b/lib/stringlist.c
index 8e92c1b207..bb2ba54a6c 100644
--- a/lib/stringlist.c
+++ b/lib/stringlist.c
@@ -1,7 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <xfuncs.h>
#include <malloc.h>
#include <errno.h>
+#include <string.h>
#include <stringlist.h>
static int string_list_compare(struct list_head *a, struct list_head *b)
@@ -60,6 +63,29 @@ int string_list_add_sorted(struct string_list *sl, const char *str)
return 0;
}
+int string_list_add_sort_uniq(struct string_list *sl, const char *str)
+{
+ struct string_list *new, *entry = sl;
+
+ string_list_for_each_entry(entry, sl) {
+ int cmp = strcmp(entry->str, str);
+
+ if (cmp < 0)
+ continue;
+ if (cmp == 0)
+ return 0;
+
+ break;
+ }
+
+ new = xmalloc(sizeof(*new));
+ new->str = xstrdup(str);
+
+ list_add_tail(&new->list, &entry->list);
+
+ return 0;
+}
+
int string_list_contains(struct string_list *sl, const char *str)
{
struct string_list *entry;
@@ -72,6 +98,35 @@ int string_list_contains(struct string_list *sl, const char *str)
return 0;
}
+char *string_list_join(const struct string_list *sl, const char *joinstr)
+{
+ struct string_list *entry;
+ size_t len = 0;
+ size_t joinstr_len = strlen(joinstr);
+ char *str, *next;
+
+ string_list_for_each_entry(entry, sl)
+ len += strlen(entry->str) + joinstr_len;
+
+ if (len == 0)
+ return strdup("");
+
+ str = malloc(len + 1);
+ if (!str)
+ return NULL;
+
+ next = str;
+
+ string_list_for_each_entry(entry, sl) {
+ next = stpcpy(next, entry->str);
+ next = stpcpy(next, joinstr);
+ }
+
+ next[-joinstr_len] = '\0';
+
+ return str;
+}
+
void string_list_print_by_column(struct string_list *sl)
{
int len = 0, num, i;
diff --git a/lib/strtox.c b/lib/strtox.c
index a5b770c8ca..76927743a7 100644
--- a/lib/strtox.c
+++ b/lib/strtox.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <linux/ctype.h>
diff --git a/lib/strverscmp.c b/lib/strverscmp.c
new file mode 100644
index 0000000000..da2d284918
--- /dev/null
+++ b/lib/strverscmp.c
@@ -0,0 +1,165 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ * Code taken from systemd src/fundamental/string-util-fundamental.c
+ * NOTE: Semantics differ from glibc strverscmp (e.g. handling of ~rc1)
+ */
+
+#include <string.h>
+#include <linux/ctype.h>
+#include <linux/export.h>
+
+static bool is_valid_version_char(char a)
+{
+ return isdigit(a) || isalpha(a) || a == '~' ||
+ a == '-' || a == '^' || a == '.';
+}
+
+int strverscmp(const char *a, const char *b)
+{
+ /* This function is similar to strverscmp(3), but it treats '-' and '.' as separators.
+ *
+ * The logic is based on rpm's rpmvercmp(), but unlike rpmvercmp(), it distiguishes e.g.
+ * '123a' and '123.a', with '123a' being newer.
+ *
+ * It allows direct comparison of strings which contain both a version and a release; e.g.
+ * '247.2-3.1.fc33.x86_64' or '5.11.0-0.rc5.20210128git76c057c84d28.137.fc34'.
+ *
+ * The input string is split into segments. Each segment is numeric or alphabetic, and may be
+ * prefixed with the following:
+ * '~' : used for pre-releases, a segment prefixed with this is the oldest,
+ * '-' : used for the separator between version and release,
+ * '^' : used for patched releases, a segment with this is newer than one with '-'.
+ * '.' : used for point releases.
+ * Note that no prefix segment is the newest. All non-supported characters are dropped, and
+ * handled as a separator of segments, e.g., '123_a' is equivalent to '123a'.
+ *
+ * By using this, version strings can be sorted like following:
+ * (older) 122.1
+ * ^ 123~rc1-1
+ * | 123
+ * | 123-a
+ * | 123-a.1
+ * | 123-1
+ * | 123-1.1
+ * | 123^post1
+ * | 123.a-1
+ * | 123.1-1
+ * v 123a-1
+ * (newer) 124-1
+ */
+
+ a = a ?: "";
+ b = b ?: "";
+
+ for (;;) {
+ const char *aa, *bb;
+ int r;
+
+ /* Drop leading invalid characters. */
+ while (*a != '\0' && !is_valid_version_char(*a))
+ a++;
+ while (*b != '\0' && !is_valid_version_char(*b))
+ b++;
+
+ /* Handle '~'. Used for pre-releases, e.g. 123~rc1, or 4.5~alpha1 */
+ if (*a == '~' || *b == '~') {
+ /* The string prefixed with '~' is older. */
+ r = compare3(*a != '~', *b != '~');
+ if (r != 0)
+ return r;
+
+ /* Now both strings are prefixed with '~'. Compare remaining strings. */
+ a++;
+ b++;
+ }
+
+ /* If at least one string reaches the end, then longer is newer.
+ * Note that except for '~' prefixed segments, a string which has more segments is newer.
+ * So, this check must be after the '~' check. */
+ if (*a == '\0' || *b == '\0')
+ return compare3(*a, *b);
+
+ /* Handle '-', which separates version and release, e.g 123.4-3.1.fc33.x86_64 */
+ if (*a == '-' || *b == '-') {
+ /* The string prefixed with '-' is older (e.g., 123-9 vs 123.1-1) */
+ r = compare3(*a != '-', *b != '-');
+ if (r != 0)
+ return r;
+
+ a++;
+ b++;
+ }
+
+ /* Handle '^'. Used for patched release. */
+ if (*a == '^' || *b == '^') {
+ r = compare3(*a != '^', *b != '^');
+ if (r != 0)
+ return r;
+
+ a++;
+ b++;
+ }
+
+ /* Handle '.'. Used for point releases. */
+ if (*a == '.' || *b == '.') {
+ r = compare3(*a != '.', *b != '.');
+ if (r != 0)
+ return r;
+
+ a++;
+ b++;
+ }
+
+ if (isdigit(*a) || isdigit(*b)) {
+ /* Find the leading numeric segments. One may be an empty string. So,
+ * numeric segments are always newer than alpha segments. */
+ for (aa = a; isdigit(*aa); aa++)
+ ;
+ for (bb = b; isdigit(*bb); bb++)
+ ;
+
+ /* Check if one of the strings was empty, but the other not. */
+ r = compare3(a != aa, b != bb);
+ if (r != 0)
+ return r;
+
+ /* Skip leading '0', to make 00123 equivalent to 123. */
+ while (*a == '0')
+ a++;
+ while (*b == '0')
+ b++;
+
+ /* To compare numeric segments without parsing their values, first compare the
+ * lengths of the segments. Eg. 12345 vs 123, longer is newer. */
+ r = compare3(aa - a, bb - b);
+ if (r != 0)
+ return r;
+
+ /* Then, compare them as strings. */
+ r = compare3(strncmp(a, b, aa - a), 0);
+ if (r != 0)
+ return r;
+ } else {
+ /* Find the leading non-numeric segments. */
+ for (aa = a; isalpha(*aa); aa++)
+ ;
+ for (bb = b; isalpha(*bb); bb++)
+ ;
+
+ /* Note that the segments are usually not NUL-terminated. */
+ r = compare3(strncmp(a, b, min(aa - a, bb - b)), 0);
+ if (r != 0)
+ return r;
+
+ /* Longer is newer, e.g. abc vs abcde. */
+ r = compare3(aa - a, bb - b);
+ if (r != 0)
+ return r;
+ }
+
+ /* The current segments are equivalent. Let's move to the next one. */
+ a = aa;
+ b = bb;
+ }
+}
+EXPORT_SYMBOL(strverscmp);
diff --git a/lib/ubsan.c b/lib/ubsan.c
index 41a5731dda..648c7cc48a 100644
--- a/lib/ubsan.c
+++ b/lib/ubsan.c
@@ -60,8 +60,8 @@ static bool was_reported(struct source_location *location)
static void print_source_location(const char *prefix,
struct source_location *loc)
{
- pr_err("%s %s:%d:%d\n", prefix, loc->file_name,
- loc->line & LINE_MASK, loc->column & COLUMN_MASK);
+ printf("%s %s:%d:%d\n", prefix, loc->file_name,
+ loc->line & LINE_MASK, loc->column & COLUMN_MASK);
}
static bool suppress_report(struct source_location *loc)
@@ -157,16 +157,16 @@ static void ubsan_prologue(struct source_location *location,
{
in_ubsan++;
- pr_err("========================================"
- "========================================\n");
+ printf("========================================"
+ "========================================\n");
print_source_location("UBSAN: Undefined behaviour in", location);
}
static void ubsan_epilogue(unsigned long *flags)
{
dump_stack();
- pr_err("========================================"
- "========================================\n");
+ printf("========================================"
+ "========================================\n");
in_ubsan--;
}
@@ -186,13 +186,13 @@ static void handle_overflow(struct overflow_data *data, void *lhs,
val_to_string(lhs_val_str, sizeof(lhs_val_str), type, lhs);
val_to_string(rhs_val_str, sizeof(rhs_val_str), type, rhs);
- pr_err("%s integer overflow:\n",
- type_is_signed(type) ? "signed" : "unsigned");
- pr_err("%s %c %s cannot be represented in type %s\n",
- lhs_val_str,
- op,
- rhs_val_str,
- type->type_name);
+ printf("%s integer overflow:\n",
+ type_is_signed(type) ? "signed" : "unsigned");
+ printf("%s %c %s cannot be represented in type %s\n",
+ lhs_val_str,
+ op,
+ rhs_val_str,
+ type->type_name);
ubsan_epilogue(&flags);
}
@@ -232,8 +232,8 @@ void __ubsan_handle_negate_overflow(struct overflow_data *data,
val_to_string(old_val_str, sizeof(old_val_str), data->type, old_val);
- pr_err("negation of %s cannot be represented in type %s:\n",
- old_val_str, data->type->type_name);
+ printf("negation of %s cannot be represented in type %s:\n",
+ old_val_str, data->type->type_name);
ubsan_epilogue(&flags);
}
@@ -254,10 +254,10 @@ void __ubsan_handle_divrem_overflow(struct overflow_data *data,
val_to_string(rhs_val_str, sizeof(rhs_val_str), data->type, rhs);
if (type_is_signed(data->type) && get_signed_val(data->type, rhs) == -1)
- pr_err("division of %s by -1 cannot be represented in type %s\n",
- rhs_val_str, data->type->type_name);
+ printf("division of %s by -1 cannot be represented in type %s\n",
+ rhs_val_str, data->type->type_name);
else
- pr_err("division by zero\n");
+ printf("division by zero\n");
ubsan_epilogue(&flags);
}
@@ -272,9 +272,9 @@ static void handle_null_ptr_deref(struct type_mismatch_data_common *data)
ubsan_prologue(data->location, &flags);
- pr_err("%s null pointer of type %s\n",
- type_check_kinds[data->type_check_kind],
- data->type->type_name);
+ printf("%s null pointer of type %s\n",
+ type_check_kinds[data->type_check_kind],
+ data->type->type_name);
ubsan_epilogue(&flags);
}
@@ -289,10 +289,10 @@ static void handle_misaligned_access(struct type_mismatch_data_common *data,
ubsan_prologue(data->location, &flags);
- pr_err("%s misaligned address %p for type %s\n",
- type_check_kinds[data->type_check_kind],
- (void *)ptr, data->type->type_name);
- pr_err("which requires %ld byte alignment\n", data->alignment);
+ printf("%s misaligned address %p for type %s\n",
+ type_check_kinds[data->type_check_kind],
+ (void *)ptr, data->type->type_name);
+ printf("which requires %ld byte alignment\n", data->alignment);
ubsan_epilogue(&flags);
}
@@ -306,10 +306,10 @@ static void handle_object_size_mismatch(struct type_mismatch_data_common *data,
return;
ubsan_prologue(data->location, &flags);
- pr_err("%s address %p with insufficient space\n",
+ printf("%s address %p with insufficient space\n",
type_check_kinds[data->type_check_kind],
(void *) ptr);
- pr_err("for an object of type %s\n", data->type->type_name);
+ printf("for an object of type %s\n", data->type->type_name);
ubsan_epilogue(&flags);
}
@@ -364,8 +364,8 @@ void __ubsan_handle_out_of_bounds(struct out_of_bounds_data *data, void *index)
ubsan_prologue(&data->location, &flags);
val_to_string(index_str, sizeof(index_str), data->index_type, index);
- pr_err("index %s is out of range for type %s\n", index_str,
- data->array_type->type_name);
+ printf("index %s is out of range for type %s\n", index_str,
+ data->array_type->type_name);
ubsan_epilogue(&flags);
}
EXPORT_SYMBOL(__ubsan_handle_out_of_bounds);
@@ -382,48 +382,28 @@ void __ubsan_handle_shift_out_of_bounds(struct shift_out_of_bounds_data *data,
if (suppress_report(&data->location))
return;
- /* This handler would be called for code shifting a one into the
- * sign bit like (1 << 31), which is all too common in barebox.
- * It's technically UB, but it's so prevalent that it's highly
- * unlikely to be treated by a compiler as anything else than the
- * standard-compliant (1U << 31). Thus check for this case here
- * and ignore it selectively
- */
- if (type_is_signed(lhs_type)) {
- s_max lhs_int, rhs_int;
-
- lhs_int = get_signed_val(lhs_type, lhs);
- rhs_int = get_signed_val(rhs_type, rhs);
-
- if (fls(lhs_int) + rhs_int == type_bit_width(lhs_type)) {
- pr_debug("signed left shift of %lld by %lld ignored.\n",
- (s64)lhs_int, (s64)rhs_int);
- return;
- }
- }
-
ubsan_prologue(&data->location, &flags);
val_to_string(rhs_str, sizeof(rhs_str), rhs_type, rhs);
val_to_string(lhs_str, sizeof(lhs_str), lhs_type, lhs);
if (val_is_negative(rhs_type, rhs))
- pr_err("shift exponent %s is negative\n", rhs_str);
+ printf("shift exponent %s is negative\n", rhs_str);
else if (get_unsigned_val(rhs_type, rhs) >=
- type_bit_width(lhs_type))
- pr_err("shift exponent %s is too large for %u-bit type %s\n",
- rhs_str,
- type_bit_width(lhs_type),
- lhs_type->type_name);
+ type_bit_width(lhs_type))
+ printf("shift exponent %s is too large for %u-bit type %s\n",
+ rhs_str,
+ type_bit_width(lhs_type),
+ lhs_type->type_name);
else if (val_is_negative(lhs_type, lhs))
- pr_err("left shift of negative value %s\n",
- lhs_str);
+ printf("left shift of negative value %s\n",
+ lhs_str);
else
- pr_err("left shift of %s by %s places cannot be"
- " represented in type %s\n",
- lhs_str, rhs_str,
- lhs_type->type_name);
+ printf("left shift of %s by %s places cannot be"
+ " represented in type %s\n",
+ lhs_str, rhs_str,
+ lhs_type->type_name);
ubsan_epilogue(&flags);
}
@@ -435,7 +415,7 @@ void __ubsan_handle_builtin_unreachable(struct unreachable_data *data)
unsigned long flags;
ubsan_prologue(&data->location, &flags);
- pr_err("calling __builtin_unreachable()\n");
+ printf("calling __builtin_unreachable()\n");
ubsan_epilogue(&flags);
panic("can't return from __builtin_unreachable()");
}
@@ -454,8 +434,8 @@ void __ubsan_handle_load_invalid_value(struct invalid_value_data *data,
val_to_string(val_str, sizeof(val_str), data->type, val);
- pr_err("load of value %s is not a valid value for type %s\n",
- val_str, data->type->type_name);
+ printf("load of value %s is not a valid value for type %s\n",
+ val_str, data->type->type_name);
ubsan_epilogue(&flags);
}
diff --git a/lib/uncompress.c b/lib/uncompress.c
index c47d319dbb..11f55c8f9f 100644
--- a/lib/uncompress.c
+++ b/lib/uncompress.c
@@ -20,24 +20,26 @@
#include <lzo.h>
#include <linux/xz.h>
#include <linux/decompress/unlz4.h>
+#include <linux/decompress/unzstd.h>
#include <errno.h>
#include <filetype.h>
#include <malloc.h>
#include <fs.h>
+#include <libfile.h>
static void *uncompress_buf;
-static unsigned int uncompress_size;
+static unsigned long uncompress_size;
void uncompress_err_stdout(char *x)
{
printf("%s\n", x);
}
-static int (*uncompress_fill_fn)(void*, unsigned int);
+static long (*uncompress_fill_fn)(void*, unsigned long);
-static int uncompress_fill(void *buf, unsigned int len)
+static long uncompress_fill(void *buf, unsigned long len)
{
- int total = 0;
+ long total = 0;
if (uncompress_size) {
int now = min(len, uncompress_size);
@@ -59,19 +61,19 @@ static int uncompress_fill(void *buf, unsigned int len)
return total;
}
-int uncompress(unsigned char *inbuf, int len,
- int(*fill)(void*, unsigned int),
- int(*flush)(void*, unsigned int),
+int uncompress(unsigned char *inbuf, long len,
+ long(*fill)(void*, unsigned long),
+ long(*flush)(void*, unsigned long),
unsigned char *output,
- int *pos,
+ long *pos,
void(*error_fn)(char *x))
{
enum filetype ft;
- int (*compfn)(unsigned char *inbuf, int len,
- int(*fill)(void*, unsigned int),
- int(*flush)(void*, unsigned int),
+ int (*compfn)(unsigned char *inbuf, long len,
+ long(*fill)(void*, unsigned long),
+ long(*flush)(void*, unsigned long),
unsigned char *output,
- int *pos,
+ long *pos,
void(*error)(char *x));
int ret;
char *err;
@@ -95,6 +97,8 @@ int uncompress(unsigned char *inbuf, int len,
ft = file_detect_type(uncompress_buf, 32);
}
+ pr_debug("Filetype detected: %s\n", file_type_to_string(ft));
+
switch (ft) {
#ifdef CONFIG_BZLIB
case filetype_bzip2:
@@ -121,6 +125,11 @@ int uncompress(unsigned char *inbuf, int len,
compfn = decompress_unxz;
break;
#endif
+#ifdef CONFIG_ZSTD_DECOMPRESS
+ case filetype_zstd_compressed:
+ compfn = unzstd;
+ break;
+#endif
default:
err = basprintf("cannot handle filetype %s",
file_type_to_string(ft));
@@ -140,12 +149,12 @@ err:
static int uncompress_infd, uncompress_outfd;
-static int fill_fd(void *buf, unsigned int len)
+static long fill_fd(void *buf, unsigned long len)
{
- return read(uncompress_infd, buf, len);
+ return read_full(uncompress_infd, buf, len);
}
-static int flush_fd(void *buf, unsigned int len)
+static long flush_fd(void *buf, unsigned long len)
{
return write(uncompress_outfd, buf, len);
}
@@ -171,3 +180,39 @@ int uncompress_fd_to_buf(int infd, void *output,
return uncompress(NULL, 0, fill_fd, NULL, output, NULL, error_fn);
}
+
+int uncompress_buf_to_fd(const void *input, size_t input_len,
+ int outfd, void(*error_fn)(char *x))
+{
+ uncompress_outfd = outfd;
+
+ return uncompress((void *)input, input_len, NULL, flush_fd,
+ NULL, NULL, error_fn);
+}
+
+ssize_t uncompress_buf_to_buf(const void *input, size_t input_len,
+ void **buf, void(*error_fn)(char *x))
+{
+ size_t size;
+ int fd, ret;
+ void *p;
+
+ fd = open("/tmp", O_TMPFILE | O_RDWR);
+ if (fd < 0)
+ return -ENODEV;
+
+ ret = uncompress_buf_to_fd(input, input_len, fd, error_fn);
+ if (ret)
+ goto close_fd;
+
+ p = read_fd(fd, &size);
+ if (p)
+ *buf = p;
+ else
+ ret = -errno;
+
+close_fd:
+ close(fd);
+
+ return ret ?: size;
+}
diff --git a/lib/unlink-recursive.c b/lib/unlink-recursive.c
index f28c6dae5b..398e43693b 100644
--- a/lib/unlink-recursive.c
+++ b/lib/unlink-recursive.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
#include <common.h>
#include <libfile.h>
#include <errno.h>
diff --git a/lib/uuid.c b/lib/uuid.c
new file mode 100644
index 0000000000..1c134bfb4b
--- /dev/null
+++ b/lib/uuid.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Unified UUID/GUID definition
+ *
+ * Copyright (C) 2009, 2016 Intel Corp.
+ * Huang Ying <ying.huang@intel.com>
+ */
+
+#include <linux/uuid.h>
+#include <module.h>
+#include <stdlib.h>
+#include <linux/ctype.h>
+#include <linux/export.h>
+
+const guid_t guid_null;
+EXPORT_SYMBOL(guid_null);
+const uuid_t uuid_null;
+EXPORT_SYMBOL(uuid_null);
+
+const u8 guid_index[16] = {3,2,1,0,5,4,7,6,8,9,10,11,12,13,14,15};
+const u8 uuid_index[16] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
+
+/**
+ * generate_random_uuid - generate a random UUID
+ * @uuid: where to put the generated UUID
+ *
+ * Random UUID interface
+ *
+ * Used to create a Boot ID or a filesystem UUID/GUID, but can be
+ * useful for other kernel drivers.
+ */
+void generate_random_uuid(unsigned char uuid[16])
+{
+ get_random_bytes(uuid, 16);
+ /* Set UUID version to 4 --- truly random generation */
+ uuid[6] = (uuid[6] & 0x0F) | 0x40;
+ /* Set the UUID variant to DCE */
+ uuid[8] = (uuid[8] & 0x3F) | 0x80;
+}
+EXPORT_SYMBOL(generate_random_uuid);
+
+void generate_random_guid(unsigned char guid[16])
+{
+ get_random_bytes(guid, 16);
+ /* Set GUID version to 4 --- truly random generation */
+ guid[7] = (guid[7] & 0x0F) | 0x40;
+ /* Set the GUID variant to DCE */
+ guid[8] = (guid[8] & 0x3F) | 0x80;
+}
+EXPORT_SYMBOL(generate_random_guid);
+
+/**
+ * uuid_is_valid - checks if a UUID string is valid
+ * @uuid: UUID string to check
+ *
+ * Description:
+ * It checks if the UUID string is following the format:
+ * xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+ *
+ * where x is a hex digit.
+ *
+ * Return: true if input is valid UUID string.
+ */
+bool uuid_is_valid(const char *uuid)
+{
+ unsigned int i;
+
+ for (i = 0; i < UUID_STRING_LEN; i++) {
+ if (i == 8 || i == 13 || i == 18 || i == 23) {
+ if (uuid[i] != '-')
+ return false;
+ } else if (!isxdigit(uuid[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+EXPORT_SYMBOL(uuid_is_valid);
+
+static int __uuid_parse(const char *uuid, __u8 b[16], const u8 ei[16])
+{
+ static const u8 si[16] = {0,2,4,6,9,11,14,16,19,21,24,26,28,30,32,34};
+ unsigned int i;
+
+ if (!uuid_is_valid(uuid))
+ return -EINVAL;
+
+ for (i = 0; i < 16; i++) {
+ int hi = hex_to_bin(uuid[si[i] + 0]);
+ int lo = hex_to_bin(uuid[si[i] + 1]);
+
+ b[ei[i]] = (hi << 4) | lo;
+ }
+
+ return 0;
+}
+
+int guid_parse(const char *uuid, guid_t *u)
+{
+ return __uuid_parse(uuid, u->b, guid_index);
+}
+EXPORT_SYMBOL(guid_parse);
+
+int uuid_parse(const char *uuid, uuid_t *u)
+{
+ return __uuid_parse(uuid, u->b, uuid_index);
+}
+EXPORT_SYMBOL(uuid_parse);
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index 6fe0283e84..1b7d568e8f 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
/*
* linux/lib/vsprintf.c
*
@@ -13,11 +15,15 @@
#include <linux/types.h>
#include <linux/string.h>
#include <linux/ctype.h>
-#include <asm-generic/div64.h>
+#include <linux/math64.h>
#include <malloc.h>
#include <kallsyms.h>
+#include <wchar.h>
+#include <of.h>
+#include <efi.h>
#include <common.h>
+#include <pbl.h>
/* we use this so that we can do without the ctype library */
#define is_digit(c) ((c) >= '0' && (c) <= '9')
@@ -41,7 +47,8 @@ static int skip_atoi(const char **s)
#define SMALL 32 /* Must be 32 == 0x20 */
#define SPECIAL 64 /* 0x */
-static char *number(char *buf, char *end, unsigned long long num, int base, int size, int precision, int type)
+static char *number(char *buf, const char *end, unsigned long long num, int base, int size,
+ int precision, int type)
{
/* we are called with base 8, 10 or 16, only, thus don't need "G..." */
static const char digits[16] = "0123456789ABCDEF"; /* "GHIJKLMNOPQRSTUVWXYZ"; */
@@ -146,7 +153,34 @@ static char *number(char *buf, char *end, unsigned long long num, int base, int
#define PAGE_SIZE 4096
#endif
-static char *string(char *buf, char *end, char *s, int field_width, int precision, int flags)
+static char *leading_spaces(char *buf, const char *end,
+ int len, int *field_width, int flags)
+{
+ if (!(flags & LEFT)) {
+ while (len < (*field_width)--) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+ }
+ }
+
+ return buf;
+}
+
+static char *trailing_spaces(char *buf, const char *end,
+ int len, int *field_width, int flags)
+{
+ while (len < (*field_width)--) {
+ if (buf < end)
+ *buf = ' ';
+ ++buf;
+ }
+
+ return buf;
+}
+
+static char *string(char *buf, const char *end, const char *s, int field_width,
+ int precision, int flags)
{
int len, i;
@@ -154,29 +188,72 @@ static char *string(char *buf, char *end, char *s, int field_width, int precisio
s = "<NULL>";
len = strnlen(s, precision);
+ buf = leading_spaces(buf, end, len, &field_width, flags);
- if (!(flags & LEFT)) {
- while (len < field_width--) {
+ for (i = 0; i < len; ++i) {
+ if (buf < end)
+ *buf = *s;
+ ++buf; ++s;
+ }
+
+ return trailing_spaces(buf, end, len, &field_width, flags);
+}
+
+static __maybe_unused char *string_array(char *buf, const char *end, char *const *s,
+ int field_width, int precision, int flags,
+ const char *separator)
+{
+ size_t i, len = strlen(separator);
+
+ while (*s) {
+ buf = string(buf, end, *s, field_width, precision, flags);
+ if (!*++s)
+ break;
+
+ for (i = 0; i < len; ++i) {
if (buf < end)
- *buf = ' ';
+ *buf = separator[i];
++buf;
}
}
+
+ return buf;
+}
+
+static char *wstring(char *buf, const char *end, const wchar_t *s, int field_width,
+ int precision, int flags)
+{
+ int len, i;
+
+ if ((unsigned long)s < PAGE_SIZE)
+ s = L"<NULL>";
+
+ len = wcsnlen(s, precision);
+ leading_spaces(buf, end, len, &field_width, flags);
+
for (i = 0; i < len; ++i) {
if (buf < end)
- *buf = *s;
+ wctomb(buf, *s);
++buf; ++s;
}
- while (len < field_width--) {
- if (buf < end)
- *buf = ' ';
- ++buf;
+
+ return trailing_spaces(buf, end, len, &field_width, flags);
+}
+
+static char *raw_pointer(char *buf, const char *end, const void *ptr, int field_width,
+ int precision, int flags)
+{
+ flags |= SMALL;
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
}
- return buf;
+ return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
}
#ifndef __PBL__
-static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+static char *symbol_string(char *buf, const char *end, const void *ptr, int field_width,
+ int precision, int flags)
{
unsigned long value = (unsigned long) ptr;
#ifdef CONFIG_KALLSYMS
@@ -191,7 +268,27 @@ static char *symbol_string(char *buf, char *end, void *ptr, int field_width, int
}
static noinline_for_stack
-char *ip4_addr_string(char *buf, char *end, const u8 *addr, int field_width,
+char *mac_address_string(char *buf, const char *end, const u8 *addr, int field_width,
+ int precision, int flags, const char *fmt)
+{
+ char mac_addr[sizeof("xx:xx:xx:xx:xx:xx")];
+ char *p = mac_addr;
+ int i;
+ char separator = ':';
+
+ for (i = 0; i < 6; i++) {
+ p = hex_byte_pack(p, addr[i]);
+
+ if (i != 5)
+ *p++ = separator;
+ }
+ *p = '\0';
+
+ return string(buf, end, mac_addr, field_width, precision, flags);
+}
+
+static noinline_for_stack
+char *ip4_addr_string(char *buf, const char *end, const u8 *addr, int field_width,
int precision, int flags, const char *fmt)
{
char ip4_addr[sizeof("255.255.255.255")];
@@ -211,9 +308,19 @@ char *ip4_addr_string(char *buf, char *end, const u8 *addr, int field_width,
return string(buf, end, ip4_addr, field_width, precision, flags);
}
+static
+char *error_string(char *buf, const char *end, const u8 *errptr, int field_width,
+ int precision, int flags, const char *fmt)
+{
+ if (!IS_ERR(errptr))
+ return raw_pointer(buf, end, errptr, field_width, precision, flags);
+
+ return string(buf, end, strerror(-PTR_ERR(errptr)), field_width, precision, flags);
+}
+
static noinline_for_stack
-char *uuid_string(char *buf, char *end, const u8 *addr, int field_width,
- int precision, int flags, const char *fmt)
+char *uuid_string(char *buf, const char *end, const u8 *addr, int field_width,
+ int precision, int flags, const char *fmt)
{
char uuid[sizeof("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")];
char *p = uuid;
@@ -223,6 +330,10 @@ char *uuid_string(char *buf, char *end, const u8 *addr, int field_width,
const u8 *index = be;
bool uc = false;
+ /* If addr == NULL output the string '<NULL>' */
+ if (!addr)
+ return string(buf, end, NULL, field_width, precision, flags);
+
switch (*(++fmt)) {
case 'L':
uc = true; /* fall-through */
@@ -258,9 +369,84 @@ char *uuid_string(char *buf, char *end, const u8 *addr, int field_width,
return string(buf, end, uuid, field_width, precision, flags);
}
+static char *device_path_string(char *buf, const char *end, const struct efi_device_path *dp,
+ int field_width, int precision, int flags)
+{
+ if (!dp)
+ return string(buf, end, NULL, field_width, precision, flags);
+
+ return buf + device_path_to_str_buf(dp, buf, end - buf);
+}
+
+static noinline_for_stack
+char *hex_string(char *buf, const char *end, const u8 *addr, int field_width,
+ int precision, int flags, const char *fmt)
+{
+ char separator;
+ int i, len;
+
+ if (field_width == 0)
+ /* nothing to print */
+ return buf;
+
+ switch (fmt[1]) {
+ case 'C':
+ separator = ':';
+ break;
+ case 'D':
+ separator = '-';
+ break;
+ case 'N':
+ separator = 0;
+ break;
+ default:
+ separator = ' ';
+ break;
+ }
+
+ len = field_width > 0 ? field_width : 1;
+
+ for (i = 0; i < len; ++i) {
+ if (buf < end)
+ *buf = hex_asc_hi(addr[i]);
+ ++buf;
+ if (buf < end)
+ *buf = hex_asc_lo(addr[i]);
+ ++buf;
+
+ if (separator && i != len - 1) {
+ if (buf < end)
+ *buf = separator;
+ ++buf;
+ }
+ }
+
+ return buf;
+}
+
static noinline_for_stack
-char *address_val(char *buf, char *end, const void *addr,
- int field_width, int precision, int flags, const char *fmt)
+char *jsonpath_string(char *buf, const char *end, char *const *path, int field_width,
+ int precision, int flags, const char *fmt)
+{
+ if ((unsigned long)path < PAGE_SIZE)
+ return string(buf, end, "<NULL>", field_width, precision, flags);
+
+ if (buf < end)
+ *buf = '$';
+ ++buf;
+
+ if (*path) {
+ if (buf < end)
+ *buf = '.';
+ ++buf;
+ }
+
+ return string_array(buf, end, path, field_width, precision, flags, ".");
+}
+
+static noinline_for_stack
+char *address_val(char *buf, const char *end, const void *addr,
+ int field_width, int precision, int flags, const char *fmt)
{
unsigned long long num;
@@ -281,15 +467,21 @@ char *address_val(char *buf, char *end, const void *addr,
return number(buf, end, num, 16, field_width, precision, flags);
}
+static noinline_for_stack
+char *device_node_string(char *buf, const char *end, const struct device_node *np,
+ int field_width, int precision, int flags, const char *fmt)
+{
+ return string(buf, end, of_node_full_name(np), field_width,
+ precision, flags);
+}
+
/*
* Show a '%p' thing. A kernel extension is that the '%p' is followed
* by an extra set of alphanumeric characters that are extended format
* specifiers.
*
- * Right now we handle:
+ * Right now we handle following Linux-compatible format specifiers:
*
- * - 'I' [4] for IPv4 addresses printed in the usual way
- * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
* - 'S' For symbolic direct pointers
* - 'U' For a 16 byte UUID/GUID, it prints the UUID/GUID in the form
* "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
@@ -309,12 +501,25 @@ char *address_val(char *buf, char *end, const void *addr,
* correctness of the format string and va_list arguments.
* - 'a[pd]' For address types [p] phys_addr_t, [d] dma_addr_t and derivatives
* (default assumed to be phys_addr_t, passed by reference)
+ * - 'I' [4] for IPv4 addresses printed in the usual way
+ * IPv4 uses dot-separated decimal without leading 0's (1.2.3.4)
+ * - 'e' For formatting error pointers as string descriptions
+ * - 'OF' For a device tree node
+ * - 'h[CDN]' For a variable-length buffer, it prints it as a hex string with
+ * a certain separator (' ' by default):
+ * C colon
+ * D dash
+ * N no separator
+ * - 'JP' For a JSON path
+ * - 'M' For a 6-byte MAC address, it prints the address in the
+ * usual colon-separated hex notation
*
- * Note: The difference between 'S' and 'F' is that on ia64 and ppc64
- * function pointers are really function descriptors, which contain a
- * pointer to the real address.
+ * Additionally, we support following barebox-specific format specifiers:
+ *
+ * - 'D' For EFI device paths
*/
-static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+static char *pointer(const char *fmt, char *buf, const char *end, const void *ptr,
+ int field_width, int precision, int flags)
{
switch (*fmt) {
case 'S':
@@ -341,23 +546,45 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field
return ip4_addr_string(buf, end, ptr, field_width, precision, flags, fmt);
}
break;
+ case 'e':
+ return error_string(buf, end, ptr, field_width, precision, flags, fmt);
+ case 'O':
+ if (IS_ENABLED(CONFIG_OFTREE))
+ return device_node_string(buf, end, ptr, field_width, precision, flags, fmt + 1);
+ break;
+ case 'h':
+ if (IS_ENABLED(CONFIG_PRINTF_HEXSTR))
+ return hex_string(buf, end, ptr, field_width, precision, flags, fmt);
+ break;
+ case 'J':
+ if (fmt[1] == 'P' && IS_ENABLED(CONFIG_JSMN))
+ return jsonpath_string(buf, end, ptr, field_width, precision, flags, fmt);
+ case 'M':
+ /* Colon separated: 00:01:02:03:04:05 */
+ return mac_address_string(buf, end, ptr, field_width, precision, flags, fmt);
+ case 'D':
+ if (IS_ENABLED(CONFIG_EFI_DEVICEPATH))
+ return device_path_string(buf, end, ptr, field_width, precision, flags);
+ break;
}
- flags |= SMALL;
- if (field_width == -1) {
- field_width = 2*sizeof(void *);
- flags |= ZEROPAD;
- }
- return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
+
+ return raw_pointer(buf, end, ptr, field_width, precision, flags);
+}
+
+static char *errno_string(char *buf, const char *end, int field_width, int precision, int flags)
+{
+ return string(buf, end, strerror(errno), field_width, precision, flags);
}
#else
-static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field_width, int precision, int flags)
+static char *pointer(const char *fmt, char *buf, const char *end, const void *ptr,
+ int field_width, int precision, int flags)
{
- flags |= SMALL;
- if (field_width == -1) {
- field_width = 2*sizeof(void *);
- flags |= ZEROPAD;
- }
- return number(buf, end, (unsigned long) ptr, 16, field_width, precision, flags);
+ return raw_pointer(buf, end, ptr, field_width, precision, flags);
+}
+
+static char *errno_string(char *buf, const char *end, int field_width, int precision, int flags)
+{
+ return buf;
}
#endif
@@ -368,10 +595,11 @@ static char *pointer(const char *fmt, char *buf, char *end, void *ptr, int field
* @fmt: The format string to use
* @args: Arguments for the format string
*
- * This function follows C99 vsnprintf, but has some extensions:
- * %pS output the name of a text symbol
- * %pF output the name of a function pointer
- * %pR output the address range in a struct resource
+ * This function generally follows C99 vsnprintf, but has some
+ * extensions and a few limitations:
+ *
+ * - ``%n`` is unsupported
+ * - ``%p*`` is handled by pointer()
*
* The return value is the number of characters which would
* be generated for the given input, excluding the trailing
@@ -499,7 +727,12 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
continue;
case 's':
- str = string(str, end, va_arg(args, char *), field_width, precision, flags);
+ if (IS_ENABLED(CONFIG_PRINTF_WCHAR) && !IN_PBL && qualifier == 'l')
+ str = wstring(str, end, va_arg(args, wchar_t *),
+ field_width, precision, flags);
+ else
+ str = string(str, end, va_arg(args, char *),
+ field_width, precision, flags);
continue;
case 'p':
@@ -549,6 +782,10 @@ int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
case 'u':
break;
+ case 'm':
+ str = errno_string(str, end, field_width, precision, flags);
+ continue;
+
default:
if (str < end)
*str = '%';
diff --git a/lib/wchar.c b/lib/wchar.c
index 4d49431e86..250538dd85 100644
--- a/lib/wchar.c
+++ b/lib/wchar.c
@@ -28,6 +28,15 @@ size_t wcslen(const wchar_t *s)
return len;
}
+size_t wcsnlen(const wchar_t * s, size_t count)
+{
+ const wchar_t *sc;
+
+ for (sc = s; count-- && *sc != L'\0'; ++sc)
+ /* nothing */;
+ return sc - s;
+}
+
wchar_t *strdup_wchar(const wchar_t *src)
{
int len = wcslen(src);
@@ -44,12 +53,30 @@ wchar_t *strdup_wchar(const wchar_t *src)
return tmp;
}
+int mbtowc(wchar_t *pwc, const char *s, size_t n)
+{
+ if (!s)
+ return 0; /* we don't mantain a non-trivial shift state */
+
+ if (n < 1)
+ return -1;
+
+ *pwc = *s;
+ return 1;
+}
+
+int wctomb(char *s, wchar_t wc)
+{
+ *s = wc & 0xFF;
+ return 1;
+}
+
char *strcpy_wchar_to_char(char *dst, const wchar_t *src)
{
char *ret = dst;
while (*src)
- *dst++ = *src++ & 0xff;
+ wctomb(dst++, *src++);
*dst = 0;
@@ -61,7 +88,7 @@ wchar_t *strcpy_char_to_wchar(wchar_t *dst, const char *src)
wchar_t *ret = dst;
while (*src)
- *dst++ = *src++;
+ mbtowc(dst++, src++, 1);
*dst = 0;
diff --git a/lib/xz/Makefile b/lib/xz/Makefile
index 86ba550733..23581d2d8c 100644
--- a/lib/xz/Makefile
+++ b/lib/xz/Makefile
@@ -1,2 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-$(CONFIG_XZ_DECOMPRESS) += xz_crc32.o xz_dec_bcj.o
obj-$(CONFIG_XZ_DECOMPRESS) += xz_dec_lzma2.o xz_dec_stream.o
diff --git a/lib/xz/xz_dec_bcj.c b/lib/xz/xz_dec_bcj.c
index d268adbc65..d40fae3416 100644
--- a/lib/xz/xz_dec_bcj.c
+++ b/lib/xz/xz_dec_bcj.c
@@ -2,7 +2,7 @@
* Branch/Call/Jump (BCJ) filter decoders
*
* Authors: Lasse Collin <lasse.collin@tukaani.org>
- * Igor Pavlov <http://7-zip.org/>
+ * Igor Pavlov <https://7-zip.org/>
*
* This file has been put into the public domain.
* You can do whatever you want with this file.
@@ -24,7 +24,8 @@ struct xz_dec_bcj {
BCJ_IA64 = 6, /* Big or little endian */
BCJ_ARM = 7, /* Little endian only */
BCJ_ARMTHUMB = 8, /* Little endian only */
- BCJ_SPARC = 9 /* Big or little endian */
+ BCJ_SPARC = 9, /* Big or little endian */
+ BCJ_ARM64 = 10 /* AArch64 */
} type;
/*
@@ -334,6 +335,45 @@ static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
}
#endif
+#ifdef XZ_DEC_ARM64
+static size_t bcj_arm64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+ size_t i;
+ uint32_t instr;
+ uint32_t addr;
+
+ for (i = 0; i + 4 <= size; i += 4) {
+ instr = get_unaligned_le32(buf + i);
+
+ if ((instr >> 26) == 0x25) {
+ /* BL instruction */
+ addr = instr - ((s->pos + (uint32_t)i) >> 2);
+ instr = 0x94000000 | (addr & 0x03FFFFFF);
+ put_unaligned_le32(instr, buf + i);
+
+ } else if ((instr & 0x9F000000) == 0x90000000) {
+ /* ADRP instruction */
+ addr = ((instr >> 29) & 3) | ((instr >> 3) & 0x1FFFFC);
+
+ /* Only convert values in the range +/-512 MiB. */
+ if ((addr + 0x020000) & 0x1C0000)
+ continue;
+
+ addr -= (s->pos + (uint32_t)i) >> 12;
+
+ instr &= 0x9000001F;
+ instr |= (addr & 3) << 29;
+ instr |= (addr & 0x03FFFC) << 3;
+ instr |= (0U - (addr & 0x020000)) & 0xE00000;
+
+ put_unaligned_le32(instr, buf + i);
+ }
+ }
+
+ return i;
+}
+#endif
+
/*
* Apply the selected BCJ filter. Update *pos and s->pos to match the amount
* of data that got filtered.
@@ -381,6 +421,11 @@ static void bcj_apply(struct xz_dec_bcj *s,
filtered = bcj_sparc(s, buf, size);
break;
#endif
+#ifdef XZ_DEC_ARM64
+ case BCJ_ARM64:
+ filtered = bcj_arm64(s, buf, size);
+ break;
+#endif
default:
/* Never reached but silence compiler warnings. */
filtered = 0;
@@ -422,7 +467,7 @@ XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
/*
* Flush pending already filtered data to the output buffer. Return
- * immediatelly if we couldn't flush everything, or if the next
+ * immediately if we couldn't flush everything, or if the next
* filter in the chain had already returned XZ_STREAM_END.
*/
if (s->temp.filtered > 0) {
@@ -554,6 +599,9 @@ XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
#ifdef XZ_DEC_SPARC
case BCJ_SPARC:
#endif
+#ifdef XZ_DEC_ARM64
+ case BCJ_ARM64:
+#endif
break;
default:
diff --git a/lib/xz/xz_private.h b/lib/xz/xz_private.h
index 85f79635f0..b14a262c92 100644
--- a/lib/xz/xz_private.h
+++ b/lib/xz/xz_private.h
@@ -37,6 +37,9 @@
# ifdef CONFIG_XZ_DEC_SPARC
# define XZ_DEC_SPARC
# endif
+# ifdef CONFIG_XZ_DEC_ARM64
+# define XZ_DEC_ARM64
+# endif
# define memeq(a, b, size) (memcmp(a, b, size) == 0)
# define memzero(buf, size) memset(buf, 0, size)
# define FREE free
@@ -98,7 +101,7 @@
#ifndef XZ_DEC_BCJ
# if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
|| defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
- || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+ || defined(XZ_DEC_ARM64) || defined(XZ_DEC_ARMTHUMB) \
|| defined(XZ_DEC_SPARC)
# define XZ_DEC_BCJ
# endif
diff --git a/lib/zlib_inflate/Makefile b/lib/zlib_inflate/Makefile
index 8e95fcd3c9..bfe12b0d6e 100644
--- a/lib/zlib_inflate/Makefile
+++ b/lib/zlib_inflate/Makefile
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
#
# This is a modified version of zlib, which does all memory
# allocation ahead of time.
diff --git a/lib/zstd/Makefile b/lib/zstd/Makefile
index 0c4f56c50c..4e8689a485 100644
--- a/lib/zstd/Makefile
+++ b/lib/zstd/Makefile
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
obj-$(CONFIG_ZSTD_DECOMPRESS) += zstd_decompress.o
ccflags-y += -O3