18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * lib/hexdump.c 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/types.h> 78c2ecf20Sopenharmony_ci#include <linux/ctype.h> 88c2ecf20Sopenharmony_ci#include <linux/errno.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/minmax.h> 118c2ecf20Sopenharmony_ci#include <linux/export.h> 128c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ciconst char hex_asc[] = "0123456789abcdef"; 158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hex_asc); 168c2ecf20Sopenharmony_ciconst char hex_asc_upper[] = "0123456789ABCDEF"; 178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hex_asc_upper); 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci/** 208c2ecf20Sopenharmony_ci * hex_to_bin - convert a hex digit to its real value 218c2ecf20Sopenharmony_ci * @ch: ascii character represents hex digit 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad 248c2ecf20Sopenharmony_ci * input. 258c2ecf20Sopenharmony_ci * 268c2ecf20Sopenharmony_ci * This function is used to load cryptographic keys, so it is coded in such a 278c2ecf20Sopenharmony_ci * way that there are no conditions or memory accesses that depend on data. 288c2ecf20Sopenharmony_ci * 298c2ecf20Sopenharmony_ci * Explanation of the logic: 308c2ecf20Sopenharmony_ci * (ch - '9' - 1) is negative if ch <= '9' 318c2ecf20Sopenharmony_ci * ('0' - 1 - ch) is negative if ch >= '0' 328c2ecf20Sopenharmony_ci * we "and" these two values, so the result is negative if ch is in the range 338c2ecf20Sopenharmony_ci * '0' ... '9' 348c2ecf20Sopenharmony_ci * we are only interested in the sign, so we do a shift ">> 8"; note that right 358c2ecf20Sopenharmony_ci * shift of a negative value is implementation-defined, so we cast the 368c2ecf20Sopenharmony_ci * value to (unsigned) before the shift --- we have 0xffffff if ch is in 378c2ecf20Sopenharmony_ci * the range '0' ... '9', 0 otherwise 388c2ecf20Sopenharmony_ci * we "and" this value with (ch - '0' + 1) --- we have a value 1 ... 10 if ch is 398c2ecf20Sopenharmony_ci * in the range '0' ... '9', 0 otherwise 408c2ecf20Sopenharmony_ci * we add this value to -1 --- we have a value 0 ... 9 if ch is in the range '0' 418c2ecf20Sopenharmony_ci * ... '9', -1 otherwise 428c2ecf20Sopenharmony_ci * the next line is similar to the previous one, but we need to decode both 438c2ecf20Sopenharmony_ci * uppercase and lowercase letters, so we use (ch & 0xdf), which converts 448c2ecf20Sopenharmony_ci * lowercase to uppercase 458c2ecf20Sopenharmony_ci */ 468c2ecf20Sopenharmony_ciint hex_to_bin(unsigned char ch) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci unsigned char cu = ch & 0xdf; 498c2ecf20Sopenharmony_ci return -1 + 508c2ecf20Sopenharmony_ci ((ch - '0' + 1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8) + 518c2ecf20Sopenharmony_ci ((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8); 528c2ecf20Sopenharmony_ci} 538c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hex_to_bin); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci/** 568c2ecf20Sopenharmony_ci * hex2bin - convert an ascii hexadecimal string to its binary representation 578c2ecf20Sopenharmony_ci * @dst: binary result 588c2ecf20Sopenharmony_ci * @src: ascii hexadecimal string 598c2ecf20Sopenharmony_ci * @count: result length 608c2ecf20Sopenharmony_ci * 618c2ecf20Sopenharmony_ci * Return 0 on success, -EINVAL in case of bad input. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ciint hex2bin(u8 *dst, const char *src, size_t count) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci while (count--) { 668c2ecf20Sopenharmony_ci int hi, lo; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci hi = hex_to_bin(*src++); 698c2ecf20Sopenharmony_ci if (unlikely(hi < 0)) 708c2ecf20Sopenharmony_ci return -EINVAL; 718c2ecf20Sopenharmony_ci lo = hex_to_bin(*src++); 728c2ecf20Sopenharmony_ci if (unlikely(lo < 0)) 738c2ecf20Sopenharmony_ci return -EINVAL; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci *dst++ = (hi << 4) | lo; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci return 0; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hex2bin); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci/** 828c2ecf20Sopenharmony_ci * bin2hex - convert binary data to an ascii hexadecimal string 838c2ecf20Sopenharmony_ci * @dst: ascii hexadecimal result 848c2ecf20Sopenharmony_ci * @src: binary data 858c2ecf20Sopenharmony_ci * @count: binary data length 868c2ecf20Sopenharmony_ci */ 878c2ecf20Sopenharmony_cichar *bin2hex(char *dst, const void *src, size_t count) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci const unsigned char *_src = src; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci while (count--) 928c2ecf20Sopenharmony_ci dst = hex_byte_pack(dst, *_src++); 938c2ecf20Sopenharmony_ci return dst; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(bin2hex); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/** 988c2ecf20Sopenharmony_ci * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory 998c2ecf20Sopenharmony_ci * @buf: data blob to dump 1008c2ecf20Sopenharmony_ci * @len: number of bytes in the @buf 1018c2ecf20Sopenharmony_ci * @rowsize: number of bytes to print per line; must be 16 or 32 1028c2ecf20Sopenharmony_ci * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) 1038c2ecf20Sopenharmony_ci * @linebuf: where to put the converted data 1048c2ecf20Sopenharmony_ci * @linebuflen: total size of @linebuf, including space for terminating NUL 1058c2ecf20Sopenharmony_ci * @ascii: include ASCII after the hex output 1068c2ecf20Sopenharmony_ci * 1078c2ecf20Sopenharmony_ci * hex_dump_to_buffer() works on one "line" of output at a time, i.e., 1088c2ecf20Sopenharmony_ci * 16 or 32 bytes of input data converted to hex + ASCII output. 1098c2ecf20Sopenharmony_ci * 1108c2ecf20Sopenharmony_ci * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data 1118c2ecf20Sopenharmony_ci * to a hex + ASCII dump at the supplied memory location. 1128c2ecf20Sopenharmony_ci * The converted output is always NUL-terminated. 1138c2ecf20Sopenharmony_ci * 1148c2ecf20Sopenharmony_ci * E.g.: 1158c2ecf20Sopenharmony_ci * hex_dump_to_buffer(frame->data, frame->len, 16, 1, 1168c2ecf20Sopenharmony_ci * linebuf, sizeof(linebuf), true); 1178c2ecf20Sopenharmony_ci * 1188c2ecf20Sopenharmony_ci * example output buffer: 1198c2ecf20Sopenharmony_ci * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * Return: 1228c2ecf20Sopenharmony_ci * The amount of bytes placed in the buffer without terminating NUL. If the 1238c2ecf20Sopenharmony_ci * output was truncated, then the return value is the number of bytes 1248c2ecf20Sopenharmony_ci * (excluding the terminating NUL) which would have been written to the final 1258c2ecf20Sopenharmony_ci * string if enough space had been available. 1268c2ecf20Sopenharmony_ci */ 1278c2ecf20Sopenharmony_ciint hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize, 1288c2ecf20Sopenharmony_ci char *linebuf, size_t linebuflen, bool ascii) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci const u8 *ptr = buf; 1318c2ecf20Sopenharmony_ci int ngroups; 1328c2ecf20Sopenharmony_ci u8 ch; 1338c2ecf20Sopenharmony_ci int j, lx = 0; 1348c2ecf20Sopenharmony_ci int ascii_column; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (rowsize != 16 && rowsize != 32) 1388c2ecf20Sopenharmony_ci rowsize = 16; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (len > rowsize) /* limit to one line at a time */ 1418c2ecf20Sopenharmony_ci len = rowsize; 1428c2ecf20Sopenharmony_ci if (!is_power_of_2(groupsize) || groupsize > 8) 1438c2ecf20Sopenharmony_ci groupsize = 1; 1448c2ecf20Sopenharmony_ci if ((len % groupsize) != 0) /* no mixed size output */ 1458c2ecf20Sopenharmony_ci groupsize = 1; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci ngroups = len / groupsize; 1488c2ecf20Sopenharmony_ci ascii_column = rowsize * 2 + rowsize / groupsize + 1; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci if (!linebuflen) 1518c2ecf20Sopenharmony_ci goto overflow1; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci if (!len) 1548c2ecf20Sopenharmony_ci goto nil; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci if (groupsize == 8) { 1578c2ecf20Sopenharmony_ci const u64 *ptr8 = buf; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci for (j = 0; j < ngroups; j++) { 1608c2ecf20Sopenharmony_ci ret = snprintf(linebuf + lx, linebuflen - lx, 1618c2ecf20Sopenharmony_ci "%s%16.16llx", j ? " " : "", 1628c2ecf20Sopenharmony_ci get_unaligned(ptr8 + j)); 1638c2ecf20Sopenharmony_ci if (ret >= linebuflen - lx) 1648c2ecf20Sopenharmony_ci goto overflow1; 1658c2ecf20Sopenharmony_ci lx += ret; 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci } else if (groupsize == 4) { 1688c2ecf20Sopenharmony_ci const u32 *ptr4 = buf; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci for (j = 0; j < ngroups; j++) { 1718c2ecf20Sopenharmony_ci ret = snprintf(linebuf + lx, linebuflen - lx, 1728c2ecf20Sopenharmony_ci "%s%8.8x", j ? " " : "", 1738c2ecf20Sopenharmony_ci get_unaligned(ptr4 + j)); 1748c2ecf20Sopenharmony_ci if (ret >= linebuflen - lx) 1758c2ecf20Sopenharmony_ci goto overflow1; 1768c2ecf20Sopenharmony_ci lx += ret; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci } else if (groupsize == 2) { 1798c2ecf20Sopenharmony_ci const u16 *ptr2 = buf; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci for (j = 0; j < ngroups; j++) { 1828c2ecf20Sopenharmony_ci ret = snprintf(linebuf + lx, linebuflen - lx, 1838c2ecf20Sopenharmony_ci "%s%4.4x", j ? " " : "", 1848c2ecf20Sopenharmony_ci get_unaligned(ptr2 + j)); 1858c2ecf20Sopenharmony_ci if (ret >= linebuflen - lx) 1868c2ecf20Sopenharmony_ci goto overflow1; 1878c2ecf20Sopenharmony_ci lx += ret; 1888c2ecf20Sopenharmony_ci } 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci for (j = 0; j < len; j++) { 1918c2ecf20Sopenharmony_ci if (linebuflen < lx + 2) 1928c2ecf20Sopenharmony_ci goto overflow2; 1938c2ecf20Sopenharmony_ci ch = ptr[j]; 1948c2ecf20Sopenharmony_ci linebuf[lx++] = hex_asc_hi(ch); 1958c2ecf20Sopenharmony_ci if (linebuflen < lx + 2) 1968c2ecf20Sopenharmony_ci goto overflow2; 1978c2ecf20Sopenharmony_ci linebuf[lx++] = hex_asc_lo(ch); 1988c2ecf20Sopenharmony_ci if (linebuflen < lx + 2) 1998c2ecf20Sopenharmony_ci goto overflow2; 2008c2ecf20Sopenharmony_ci linebuf[lx++] = ' '; 2018c2ecf20Sopenharmony_ci } 2028c2ecf20Sopenharmony_ci if (j) 2038c2ecf20Sopenharmony_ci lx--; 2048c2ecf20Sopenharmony_ci } 2058c2ecf20Sopenharmony_ci if (!ascii) 2068c2ecf20Sopenharmony_ci goto nil; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci while (lx < ascii_column) { 2098c2ecf20Sopenharmony_ci if (linebuflen < lx + 2) 2108c2ecf20Sopenharmony_ci goto overflow2; 2118c2ecf20Sopenharmony_ci linebuf[lx++] = ' '; 2128c2ecf20Sopenharmony_ci } 2138c2ecf20Sopenharmony_ci for (j = 0; j < len; j++) { 2148c2ecf20Sopenharmony_ci if (linebuflen < lx + 2) 2158c2ecf20Sopenharmony_ci goto overflow2; 2168c2ecf20Sopenharmony_ci ch = ptr[j]; 2178c2ecf20Sopenharmony_ci linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.'; 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_cinil: 2208c2ecf20Sopenharmony_ci linebuf[lx] = '\0'; 2218c2ecf20Sopenharmony_ci return lx; 2228c2ecf20Sopenharmony_cioverflow2: 2238c2ecf20Sopenharmony_ci linebuf[lx++] = '\0'; 2248c2ecf20Sopenharmony_cioverflow1: 2258c2ecf20Sopenharmony_ci return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ciEXPORT_SYMBOL(hex_dump_to_buffer); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci#ifdef CONFIG_PRINTK 2308c2ecf20Sopenharmony_ci/** 2318c2ecf20Sopenharmony_ci * print_hex_dump - print a text hex dump to syslog for a binary blob of data 2328c2ecf20Sopenharmony_ci * @level: kernel log level (e.g. KERN_DEBUG) 2338c2ecf20Sopenharmony_ci * @prefix_str: string to prefix each line with; 2348c2ecf20Sopenharmony_ci * caller supplies trailing spaces for alignment if desired 2358c2ecf20Sopenharmony_ci * @prefix_type: controls whether prefix of an offset, address, or none 2368c2ecf20Sopenharmony_ci * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) 2378c2ecf20Sopenharmony_ci * @rowsize: number of bytes to print per line; must be 16 or 32 2388c2ecf20Sopenharmony_ci * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) 2398c2ecf20Sopenharmony_ci * @buf: data blob to dump 2408c2ecf20Sopenharmony_ci * @len: number of bytes in the @buf 2418c2ecf20Sopenharmony_ci * @ascii: include ASCII after the hex output 2428c2ecf20Sopenharmony_ci * 2438c2ecf20Sopenharmony_ci * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump 2448c2ecf20Sopenharmony_ci * to the kernel log at the specified kernel log level, with an optional 2458c2ecf20Sopenharmony_ci * leading prefix. 2468c2ecf20Sopenharmony_ci * 2478c2ecf20Sopenharmony_ci * print_hex_dump() works on one "line" of output at a time, i.e., 2488c2ecf20Sopenharmony_ci * 16 or 32 bytes of input data converted to hex + ASCII output. 2498c2ecf20Sopenharmony_ci * print_hex_dump() iterates over the entire input @buf, breaking it into 2508c2ecf20Sopenharmony_ci * "line size" chunks to format and print. 2518c2ecf20Sopenharmony_ci * 2528c2ecf20Sopenharmony_ci * E.g.: 2538c2ecf20Sopenharmony_ci * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, 2548c2ecf20Sopenharmony_ci * 16, 1, frame->data, frame->len, true); 2558c2ecf20Sopenharmony_ci * 2568c2ecf20Sopenharmony_ci * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode: 2578c2ecf20Sopenharmony_ci * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO 2588c2ecf20Sopenharmony_ci * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode: 2598c2ecf20Sopenharmony_ci * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~. 2608c2ecf20Sopenharmony_ci */ 2618c2ecf20Sopenharmony_civoid print_hex_dump(const char *level, const char *prefix_str, int prefix_type, 2628c2ecf20Sopenharmony_ci int rowsize, int groupsize, 2638c2ecf20Sopenharmony_ci const void *buf, size_t len, bool ascii) 2648c2ecf20Sopenharmony_ci{ 2658c2ecf20Sopenharmony_ci const u8 *ptr = buf; 2668c2ecf20Sopenharmony_ci int i, linelen, remaining = len; 2678c2ecf20Sopenharmony_ci unsigned char linebuf[32 * 3 + 2 + 32 + 1]; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (rowsize != 16 && rowsize != 32) 2708c2ecf20Sopenharmony_ci rowsize = 16; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci for (i = 0; i < len; i += rowsize) { 2738c2ecf20Sopenharmony_ci linelen = min(remaining, rowsize); 2748c2ecf20Sopenharmony_ci remaining -= rowsize; 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, 2778c2ecf20Sopenharmony_ci linebuf, sizeof(linebuf), ascii); 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci switch (prefix_type) { 2808c2ecf20Sopenharmony_ci case DUMP_PREFIX_ADDRESS: 2818c2ecf20Sopenharmony_ci printk("%s%s%p: %s\n", 2828c2ecf20Sopenharmony_ci level, prefix_str, ptr + i, linebuf); 2838c2ecf20Sopenharmony_ci break; 2848c2ecf20Sopenharmony_ci case DUMP_PREFIX_OFFSET: 2858c2ecf20Sopenharmony_ci printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); 2868c2ecf20Sopenharmony_ci break; 2878c2ecf20Sopenharmony_ci default: 2888c2ecf20Sopenharmony_ci printk("%s%s%s\n", level, prefix_str, linebuf); 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ciEXPORT_SYMBOL(print_hex_dump); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci#endif /* defined(CONFIG_PRINTK) */ 296