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