diff options
Diffstat (limited to 'lib')
101 files changed, 4091 insertions, 525 deletions
diff --git a/lib/Kconfig b/lib/Kconfig index e5831ecdb9..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,6 +62,9 @@ config XZ_DEC_ARMTHUMB config XZ_DEC_SPARC bool +config XZ_DEC_ARM64 + bool + config REED_SOLOMON bool @@ -154,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 @@ -188,4 +221,14 @@ config ARCH_HAS_ZERO_PAGE 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 9b37d847e0..54866f59cc 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,13 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-y += bcd.o obj-$(CONFIG_BOOTSTRAP) += bootstrap/ 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 += misc.o obj-$(CONFIG_PARAMETER) += parameter.o @@ -17,13 +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,6 +94,8 @@ 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 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); @@ -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 6d28b1cb4d..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; @@ -89,8 +88,7 @@ void* bootstrap_read_devfs(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(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; @@ -152,7 +150,7 @@ free_memory: } delete_devfs_partition: - devfs_del_partition(partname); + cdevfs_del_partition(partition); return result; } 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 5e95760bae..f1bfda641c 100644 --- a/lib/cmdlinepart.c +++ b/lib/cmdlinepart.c @@ -29,7 +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; + unsigned long flags = DEVFS_PARTITION_FOR_FIXUP; struct cdev *cdev; memset(buf, 0, PATH_MAX); 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(¶ms, 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); +} @@ -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; @@ -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 8523bad9a7..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; @@ -414,44 +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); - } -} -#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; + free(pglob->gl_pathv[i]); + free(pglob->gl_pathv); } - - 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 */ 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 93f345e881..ae078536e3 100644 --- a/lib/hexdump.c +++ b/lib/hexdump.c @@ -4,11 +4,13 @@ */ #include <common.h> +#include <driver.h> #include <linux/types.h> #include <linux/ctype.h> #include <linux/log2.h> -#include <printk.h> +#include <linux/printk.h> #include <asm/unaligned.h> +#include <pbl.h> const char hex_asc[] = "0123456789abcdef"; EXPORT_SYMBOL(hex_asc); @@ -235,13 +237,20 @@ EXPORT_SYMBOL(hex_dump_to_buffer); * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode: * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~. */ -void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, - int rowsize, int groupsize, - const void *buf, size_t len, bool ascii) +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; @@ -255,16 +264,21 @@ void print_hex_dump(const char *level, const char *prefix_str, int prefix_type, switch (prefix_type) { case DUMP_PREFIX_ADDRESS: - printk("%s%s%p: %s\n", - level, prefix_str, ptr + i, linebuf); + printk("%s%s%s%p: %s\n", level, name, prefix_str, + ptr + i, linebuf); break; case DUMP_PREFIX_OFFSET: - printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); + printk("%s%s%s%.8x: %s\n", level, name, prefix_str, + i, linebuf); break; default: - printk("%s%s%s\n", level, prefix_str, linebuf); + printk("%s%s%s%s\n", level, name, prefix_str, + linebuf); break; } } + + if (dev) + free(name); } -EXPORT_SYMBOL(print_hex_dump); +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 c375c78d63..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; 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 index 7a18cf95be..e96638304c 100644 --- a/lib/kasan/Kconfig +++ b/lib/kasan/Kconfig @@ -1,4 +1,4 @@ -source "scripts/Kconfig.include" +# SPDX-License-Identifier: GPL-2.0-only config HAVE_ARCH_KASAN bool diff --git a/lib/kasan/Makefile b/lib/kasan/Makefile index 31e9d890d5..e3f4bb61f9 100644 --- a/lib/kasan/Makefile +++ b/lib/kasan/Makefile @@ -1,3 +1,4 @@ +# 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 diff --git a/lib/kasan/generic.c b/lib/kasan/generic.c index b33a6c1a6c..3709b8da9a 100644 --- a/lib/kasan/generic.c +++ b/lib/kasan/generic.c @@ -14,14 +14,13 @@ * */ -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - #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 @@ -160,6 +159,9 @@ static __always_inline bool check_memory_region_inline(unsigned long addr, if (addr < kasan_shadow_start) return true; + if (addr > kasan_shadowed_end) + return true; + if (unlikely(size == 0)) return true; @@ -180,6 +182,7 @@ void kasan_init(unsigned long membase, unsigned long memsize, { kasan_shadow_start = membase; kasan_shadow_base = shadow_base; + kasan_shadowed_end = membase + memsize - 1; kasan_unpoison_shadow((void *)membase, memsize); kasan_initialized = true; diff --git a/lib/kasan/report.c b/lib/kasan/report.c index b7b2d032ee..a9050546e7 100644 --- a/lib/kasan/report.c +++ b/lib/kasan/report.c @@ -17,7 +17,7 @@ #include <common.h> #include <linux/bitops.h> #include <linux/kernel.h> -#include <printk.h> +#include <linux/printk.h> #include <asm-generic/sections.h> #include "kasan.h" @@ -48,9 +48,9 @@ EXPORT_SYMBOL_GPL(kasan_restore_multi_shot); static void print_error_description(struct kasan_access_info *info) { - pr_err("BUG: KASAN: %s in %pS\n", + eprintf("BUG: KASAN: %s in %pS\n", get_bug_type(info), (void *)info->ip); - pr_err("%s of size %zu at addr %px\n", + eprintf("%s of size %zu at addr %px\n", info->is_write ? "Write" : "Read", info->access_size, info->access_addr); } @@ -61,12 +61,12 @@ static void start_report(unsigned long *flags) * Make sure we don't end up in loop. */ kasan_disable_current(); - pr_err("==================================================================\n"); + eprintf("==================================================================\n"); } static void end_report(unsigned long *flags) { - pr_err("==================================================================\n"); + eprintf("==================================================================\n"); kasan_enable_current(); } @@ -80,11 +80,11 @@ static inline bool kernel_or_module_addr(const void *addr) static void print_address_description(void *addr, u8 tag) { dump_stack(); - pr_err("\n"); + eprintf("\n"); if (kernel_or_module_addr(addr)) { - pr_err("The buggy address belongs to the variable:\n"); - pr_err(" %pS\n", addr); + eprintf("The buggy address belongs to the variable:\n"); + eprintf(" %pS\n", addr); } } @@ -112,7 +112,7 @@ static void print_shadow_for_address(const void *addr) SHADOW_BYTES_PER_ROW) - SHADOW_ROWS_AROUND_ADDR * SHADOW_BYTES_PER_ROW; - pr_err("Memory state around the buggy address:\n"); + 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); @@ -132,7 +132,7 @@ static void print_shadow_for_address(const void *addr) shadow_buf, SHADOW_BYTES_PER_ROW, 0); if (row_is_guilty(shadow_row, shadow)) - printf("%*c\n", + eprintf("%*c\n", shadow_pointer_offset(shadow_row, shadow), '^'); @@ -172,11 +172,11 @@ static void __kasan_report(unsigned long addr, size_t size, bool is_write, start_report(&flags); print_error_description(&info); - pr_err("\n"); + eprintf("\n"); if (addr_has_shadow(untagged_addr)) { print_address_description(untagged_addr, get_tag(tagged_addr)); - pr_err("\n"); + eprintf("\n"); print_shadow_for_address(info.first_bad_addr); } else { dump_stack(); diff --git a/lib/kasan/test_kasan.c b/lib/kasan/test_kasan.c index e472bb3499..a74251a6d9 100644 --- a/lib/kasan/test_kasan.c +++ b/lib/kasan/test_kasan.c @@ -38,6 +38,8 @@ static noinline void malloc_oob_right(void) return; } + OPTIMIZER_HIDE_VAR(ptr); + ptr[size] = 'x'; free(ptr); @@ -55,6 +57,8 @@ static noinline void malloc_oob_left(void) return; } + OPTIMIZER_HIDE_VAR(ptr); + *ptr = *(ptr - 1); free(ptr); } @@ -75,6 +79,8 @@ static noinline void malloc_oob_realloc_more(void) return; } + OPTIMIZER_HIDE_VAR(ptr2); + ptr2[size2] = 'x'; free(ptr2); @@ -95,6 +101,8 @@ static noinline void malloc_oob_realloc_less(void) return; } + OPTIMIZER_HIDE_VAR(ptr2); + ptr2[size2] = 'x'; free(ptr2); @@ -115,6 +123,9 @@ static noinline void malloc_oob_16(void) free(ptr2); return; } + + OPTIMIZER_HIDE_VAR(ptr1); + *ptr1 = *ptr2; free(ptr1); free(ptr2); @@ -290,6 +301,8 @@ static noinline void kasan_alloca_oob_left(void) 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; } @@ -474,5 +487,6 @@ static int do_kasan_test(int argc, char *argv[]) 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 4ab8db11ad..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; - goto err_out; - } - - fd = open(filename, O_RDONLY); - if (fd < 0) { - ret = fd; + ret = errno_set(-ENOMEM); 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 @@ -339,7 +448,7 @@ int copy_file(const char *src, const char *dst, int verbose) 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,7 +457,7 @@ 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; } @@ -359,7 +468,7 @@ int copy_file(const char *src, const char *dst, int verbose) 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; } @@ -494,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); @@ -539,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; @@ -557,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/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 f5122b2592..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; 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 4149d4ff6c..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 = --export-width=64 +OPTS_barebox-logo-w64 = -resize 64 bblogo-$(CONFIG_BAREBOX_LOGO_64) += barebox-logo-w64 -OPTS_barebox-logo-w240.bblogo = --export-width=240 +OPTS_barebox-logo-w240 = -resize 240 bblogo-$(CONFIG_BAREBOX_LOGO_240) += barebox-logo-w240 -OPTS_barebox-logo-w320.bblogo = --export-width=320 +OPTS_barebox-logo-w320 = -resize 320 bblogo-$(CONFIG_BAREBOX_LOGO_320) += barebox-logo-w320 -OPTS_barebox-logo-w400.bblogo = --export-width=400 +OPTS_barebox-logo-w400 = -resize 400 bblogo-$(CONFIG_BAREBOX_LOGO_400) += barebox-logo-w400 -OPTS_barebox-logo-w640.bblogo = --export-width=640 +OPTS_barebox-logo-w640 = -resize 640 bblogo-$(CONFIG_BAREBOX_LOGO_640) += barebox-logo-w640 obj-y += $(patsubst %,%.bblogo.o,$(bblogo-y)) @@ -24,7 +25,8 @@ obj-$(CONFIG_BAREBOX_LOGO) += logo.o 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,18 +40,44 @@ cmd_logo_S = \ %.bblogo.S: %.bblogo FORCE $(call if_changed,logo_S) -# Inkscape 0.92.4 supports -z -e but Inkscape 1.0 uses --export-type=png -INKSCAPEOPTS += $(call try-run, inkscape -z -e -,-z -e -,--export-type=png) -# Inkscape 1.0 supports -o - -INKSCAPEOPTS += $(call try-run, inkscape -o -,-o -,) +quiet_cmd_logo = LOGO $@ + cmd_logo = cp $< $@ -quiet_cmd_logo = LOGO.S $@ -cmd_logo = \ -( \ - inkscape $(OPTS_$(@F)) $(INKSCAPEOPTS) $< > $@; \ -) - -%.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 17b0083236..2ead1321e8 100644 --- a/lib/lzo/Kconfig +++ b/lib/lzo/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + config LZO_DECOMPRESS bool "include lzo uncompression support" select UNCOMPRESS diff --git a/lib/lzo/Makefile b/lib/lzo/Makefile index 0e576a1c10..4ab28ff6f5 100644 --- a/lib/lzo/Makefile +++ b/lib/lzo/Makefile @@ -1 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + obj-$(CONFIG_LZO_DECOMPRESS) += lzo1x_decompress_safe.o 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 index c2c892dd55..2830dedb2f 100644 --- a/lib/math/Makefile +++ b/lib/math/Makefile @@ -1,2 +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/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 173420c58a..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; @@ -755,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; @@ -778,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; @@ -795,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; @@ -829,14 +840,14 @@ struct param_mac { }; 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]; @@ -865,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; @@ -876,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; @@ -907,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 @@ -925,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 ce30223bac..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__); @@ -1175,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; } @@ -1217,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; } @@ -1244,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); } @@ -1580,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; @@ -1601,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; @@ -1640,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++; diff --git a/lib/readline.c b/lib/readline.c index 25aa99b95e..92bec3d1d8 100644 --- a/lib/readline.c +++ b/lib/readline.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only + #include <common.h> #include <readkey.h> #include <init.h> @@ -228,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 1be06ea780..1b624bcb9a 100644 --- a/lib/show_progress.c +++ b/lib/show_progress.c @@ -57,3 +57,31 @@ void init_progression_bar(loff_t 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 dbb66fe4d2..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 * @@ -21,16 +22,31 @@ #include <linux/types.h> #include <string.h> #include <linux/ctype.h> +#include <asm/word-at-a-time.h> #include <malloc.h> -#ifndef __HAVE_ARCH_STRNICMP +#ifndef __HAVE_ARCH_STRCASECMP +int strcasecmp(const char *s1, const char *s2) +{ + int c1, c2; + + 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; @@ -52,34 +68,6 @@ int strnicmp(const char *s1, const char *s2, size_t len) } while (--len); return (int)c1 - (int)c2; } -EXPORT_SYMBOL(strnicmp); -#endif - -#ifndef __HAVE_ARCH_STRCASECMP -int strcasecmp(const char *s1, const char *s2) -{ - int c1, c2; - - do { - c1 = tolower(*s1++); - c2 = tolower(*s2++); - } while (c1 == c2 && c1 != 0); - return c1 - c2; -} -EXPORT_SYMBOL(strcasecmp); -#endif - -#ifndef __HAVE_ARCH_STRNCASECMP -int strncasecmp(const char *s1, const char *s2, size_t n) -{ - int c1, c2; - - do { - c1 = tolower(*s1++); - c2 = tolower(*s2++); - } while ((--n > 0) && c1 == c2 && c1 != 0); - return c1 - c2; -} EXPORT_SYMBOL(strncasecmp); #endif @@ -100,6 +88,76 @@ char * strcpy(char * dest,const char *src) #endif EXPORT_SYMBOL(strcpy); +#ifndef __HAVE_ARCH_STRSCPY +ssize_t strscpy(char *dest, const char *src, size_t count) +{ + 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 + + /* + * 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(strscpy); +#endif + /** * stpcpy - Copy a %NUL terminated string, but return pointer to %NUL * @dest: Where to copy the string to @@ -274,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 @@ -529,7 +605,7 @@ void *__default_memset(void * s, int c, size_t count) } EXPORT_SYMBOL(__default_memset); -void __no_sanitize_address *__nokasan_default_memset(void * s, int c, size_t count) +void __prereloc __no_sanitize_address *__nokasan_default_memset(void * s, int c, size_t count) { char *xs = (char *) s; @@ -584,6 +660,11 @@ 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 /** @@ -866,6 +947,15 @@ int strtobool(const char *str, int *val) } 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 @@ -910,3 +1000,52 @@ char *parse_assignment(char *str) 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 719fecdaa4..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) @@ -95,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/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 1d82adc733..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 * @@ -16,8 +18,12 @@ #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') @@ -147,6 +153,32 @@ static char *number(char *buf, const char *end, unsigned long long num, int base #define PAGE_SIZE 4096 #endif +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) { @@ -156,25 +188,56 @@ static char *string(char *buf, const char *end, const char *s, int field_width, 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 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, @@ -205,6 +268,26 @@ static char *symbol_string(char *buf, const char *end, const void *ptr, int fiel } static noinline_for_stack +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) { @@ -247,6 +330,10 @@ char *uuid_string(char *buf, const 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 */ @@ -282,6 +369,81 @@ char *uuid_string(char *buf, const 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 *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) @@ -305,15 +467,21 @@ char *address_val(char *buf, const 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" @@ -333,10 +501,22 @@ char *address_val(char *buf, const 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, const char *end, const void *ptr, int field_width, int precision, int flags) @@ -368,17 +548,44 @@ static char *pointer(const char *fmt, char *buf, const char *end, const void *pt 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; } 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, const char *end, const void *ptr, int field_width, int precision, int 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 /** @@ -388,10 +595,11 @@ static char *pointer(const char *fmt, char *buf, const char *end, const void *pt * @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 @@ -519,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': @@ -569,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 |