162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * lib/hexdump.c 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include <linux/types.h> 762306a36Sopenharmony_ci#include <linux/ctype.h> 862306a36Sopenharmony_ci#include <linux/errno.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/minmax.h> 1162306a36Sopenharmony_ci#include <linux/export.h> 1262306a36Sopenharmony_ci#include <asm/unaligned.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ciconst char hex_asc[] = "0123456789abcdef"; 1562306a36Sopenharmony_ciEXPORT_SYMBOL(hex_asc); 1662306a36Sopenharmony_ciconst char hex_asc_upper[] = "0123456789ABCDEF"; 1762306a36Sopenharmony_ciEXPORT_SYMBOL(hex_asc_upper); 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/** 2062306a36Sopenharmony_ci * hex_to_bin - convert a hex digit to its real value 2162306a36Sopenharmony_ci * @ch: ascii character represents hex digit 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad 2462306a36Sopenharmony_ci * input. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * This function is used to load cryptographic keys, so it is coded in such a 2762306a36Sopenharmony_ci * way that there are no conditions or memory accesses that depend on data. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Explanation of the logic: 3062306a36Sopenharmony_ci * (ch - '9' - 1) is negative if ch <= '9' 3162306a36Sopenharmony_ci * ('0' - 1 - ch) is negative if ch >= '0' 3262306a36Sopenharmony_ci * we "and" these two values, so the result is negative if ch is in the range 3362306a36Sopenharmony_ci * '0' ... '9' 3462306a36Sopenharmony_ci * we are only interested in the sign, so we do a shift ">> 8"; note that right 3562306a36Sopenharmony_ci * shift of a negative value is implementation-defined, so we cast the 3662306a36Sopenharmony_ci * value to (unsigned) before the shift --- we have 0xffffff if ch is in 3762306a36Sopenharmony_ci * the range '0' ... '9', 0 otherwise 3862306a36Sopenharmony_ci * we "and" this value with (ch - '0' + 1) --- we have a value 1 ... 10 if ch is 3962306a36Sopenharmony_ci * in the range '0' ... '9', 0 otherwise 4062306a36Sopenharmony_ci * we add this value to -1 --- we have a value 0 ... 9 if ch is in the range '0' 4162306a36Sopenharmony_ci * ... '9', -1 otherwise 4262306a36Sopenharmony_ci * the next line is similar to the previous one, but we need to decode both 4362306a36Sopenharmony_ci * uppercase and lowercase letters, so we use (ch & 0xdf), which converts 4462306a36Sopenharmony_ci * lowercase to uppercase 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_ciint hex_to_bin(unsigned char ch) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci unsigned char cu = ch & 0xdf; 4962306a36Sopenharmony_ci return -1 + 5062306a36Sopenharmony_ci ((ch - '0' + 1) & (unsigned)((ch - '9' - 1) & ('0' - 1 - ch)) >> 8) + 5162306a36Sopenharmony_ci ((cu - 'A' + 11) & (unsigned)((cu - 'F' - 1) & ('A' - 1 - cu)) >> 8); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ciEXPORT_SYMBOL(hex_to_bin); 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * hex2bin - convert an ascii hexadecimal string to its binary representation 5762306a36Sopenharmony_ci * @dst: binary result 5862306a36Sopenharmony_ci * @src: ascii hexadecimal string 5962306a36Sopenharmony_ci * @count: result length 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * Return 0 on success, -EINVAL in case of bad input. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ciint hex2bin(u8 *dst, const char *src, size_t count) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci while (count--) { 6662306a36Sopenharmony_ci int hi, lo; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci hi = hex_to_bin(*src++); 6962306a36Sopenharmony_ci if (unlikely(hi < 0)) 7062306a36Sopenharmony_ci return -EINVAL; 7162306a36Sopenharmony_ci lo = hex_to_bin(*src++); 7262306a36Sopenharmony_ci if (unlikely(lo < 0)) 7362306a36Sopenharmony_ci return -EINVAL; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci *dst++ = (hi << 4) | lo; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci return 0; 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ciEXPORT_SYMBOL(hex2bin); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci/** 8262306a36Sopenharmony_ci * bin2hex - convert binary data to an ascii hexadecimal string 8362306a36Sopenharmony_ci * @dst: ascii hexadecimal result 8462306a36Sopenharmony_ci * @src: binary data 8562306a36Sopenharmony_ci * @count: binary data length 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cichar *bin2hex(char *dst, const void *src, size_t count) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci const unsigned char *_src = src; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci while (count--) 9262306a36Sopenharmony_ci dst = hex_byte_pack(dst, *_src++); 9362306a36Sopenharmony_ci return dst; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ciEXPORT_SYMBOL(bin2hex); 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * hex_dump_to_buffer - convert a blob of data to "hex ASCII" in memory 9962306a36Sopenharmony_ci * @buf: data blob to dump 10062306a36Sopenharmony_ci * @len: number of bytes in the @buf 10162306a36Sopenharmony_ci * @rowsize: number of bytes to print per line; must be 16 or 32 10262306a36Sopenharmony_ci * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) 10362306a36Sopenharmony_ci * @linebuf: where to put the converted data 10462306a36Sopenharmony_ci * @linebuflen: total size of @linebuf, including space for terminating NUL 10562306a36Sopenharmony_ci * @ascii: include ASCII after the hex output 10662306a36Sopenharmony_ci * 10762306a36Sopenharmony_ci * hex_dump_to_buffer() works on one "line" of output at a time, i.e., 10862306a36Sopenharmony_ci * 16 or 32 bytes of input data converted to hex + ASCII output. 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * Given a buffer of u8 data, hex_dump_to_buffer() converts the input data 11162306a36Sopenharmony_ci * to a hex + ASCII dump at the supplied memory location. 11262306a36Sopenharmony_ci * The converted output is always NUL-terminated. 11362306a36Sopenharmony_ci * 11462306a36Sopenharmony_ci * E.g.: 11562306a36Sopenharmony_ci * hex_dump_to_buffer(frame->data, frame->len, 16, 1, 11662306a36Sopenharmony_ci * linebuf, sizeof(linebuf), true); 11762306a36Sopenharmony_ci * 11862306a36Sopenharmony_ci * example output buffer: 11962306a36Sopenharmony_ci * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO 12062306a36Sopenharmony_ci * 12162306a36Sopenharmony_ci * Return: 12262306a36Sopenharmony_ci * The amount of bytes placed in the buffer without terminating NUL. If the 12362306a36Sopenharmony_ci * output was truncated, then the return value is the number of bytes 12462306a36Sopenharmony_ci * (excluding the terminating NUL) which would have been written to the final 12562306a36Sopenharmony_ci * string if enough space had been available. 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ciint hex_dump_to_buffer(const void *buf, size_t len, int rowsize, int groupsize, 12862306a36Sopenharmony_ci char *linebuf, size_t linebuflen, bool ascii) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci const u8 *ptr = buf; 13162306a36Sopenharmony_ci int ngroups; 13262306a36Sopenharmony_ci u8 ch; 13362306a36Sopenharmony_ci int j, lx = 0; 13462306a36Sopenharmony_ci int ascii_column; 13562306a36Sopenharmony_ci int ret; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci if (rowsize != 16 && rowsize != 32) 13862306a36Sopenharmony_ci rowsize = 16; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (len > rowsize) /* limit to one line at a time */ 14162306a36Sopenharmony_ci len = rowsize; 14262306a36Sopenharmony_ci if (!is_power_of_2(groupsize) || groupsize > 8) 14362306a36Sopenharmony_ci groupsize = 1; 14462306a36Sopenharmony_ci if ((len % groupsize) != 0) /* no mixed size output */ 14562306a36Sopenharmony_ci groupsize = 1; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci ngroups = len / groupsize; 14862306a36Sopenharmony_ci ascii_column = rowsize * 2 + rowsize / groupsize + 1; 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci if (!linebuflen) 15162306a36Sopenharmony_ci goto overflow1; 15262306a36Sopenharmony_ci 15362306a36Sopenharmony_ci if (!len) 15462306a36Sopenharmony_ci goto nil; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (groupsize == 8) { 15762306a36Sopenharmony_ci const u64 *ptr8 = buf; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci for (j = 0; j < ngroups; j++) { 16062306a36Sopenharmony_ci ret = snprintf(linebuf + lx, linebuflen - lx, 16162306a36Sopenharmony_ci "%s%16.16llx", j ? " " : "", 16262306a36Sopenharmony_ci get_unaligned(ptr8 + j)); 16362306a36Sopenharmony_ci if (ret >= linebuflen - lx) 16462306a36Sopenharmony_ci goto overflow1; 16562306a36Sopenharmony_ci lx += ret; 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } else if (groupsize == 4) { 16862306a36Sopenharmony_ci const u32 *ptr4 = buf; 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci for (j = 0; j < ngroups; j++) { 17162306a36Sopenharmony_ci ret = snprintf(linebuf + lx, linebuflen - lx, 17262306a36Sopenharmony_ci "%s%8.8x", j ? " " : "", 17362306a36Sopenharmony_ci get_unaligned(ptr4 + j)); 17462306a36Sopenharmony_ci if (ret >= linebuflen - lx) 17562306a36Sopenharmony_ci goto overflow1; 17662306a36Sopenharmony_ci lx += ret; 17762306a36Sopenharmony_ci } 17862306a36Sopenharmony_ci } else if (groupsize == 2) { 17962306a36Sopenharmony_ci const u16 *ptr2 = buf; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci for (j = 0; j < ngroups; j++) { 18262306a36Sopenharmony_ci ret = snprintf(linebuf + lx, linebuflen - lx, 18362306a36Sopenharmony_ci "%s%4.4x", j ? " " : "", 18462306a36Sopenharmony_ci get_unaligned(ptr2 + j)); 18562306a36Sopenharmony_ci if (ret >= linebuflen - lx) 18662306a36Sopenharmony_ci goto overflow1; 18762306a36Sopenharmony_ci lx += ret; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci } else { 19062306a36Sopenharmony_ci for (j = 0; j < len; j++) { 19162306a36Sopenharmony_ci if (linebuflen < lx + 2) 19262306a36Sopenharmony_ci goto overflow2; 19362306a36Sopenharmony_ci ch = ptr[j]; 19462306a36Sopenharmony_ci linebuf[lx++] = hex_asc_hi(ch); 19562306a36Sopenharmony_ci if (linebuflen < lx + 2) 19662306a36Sopenharmony_ci goto overflow2; 19762306a36Sopenharmony_ci linebuf[lx++] = hex_asc_lo(ch); 19862306a36Sopenharmony_ci if (linebuflen < lx + 2) 19962306a36Sopenharmony_ci goto overflow2; 20062306a36Sopenharmony_ci linebuf[lx++] = ' '; 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci if (j) 20362306a36Sopenharmony_ci lx--; 20462306a36Sopenharmony_ci } 20562306a36Sopenharmony_ci if (!ascii) 20662306a36Sopenharmony_ci goto nil; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci while (lx < ascii_column) { 20962306a36Sopenharmony_ci if (linebuflen < lx + 2) 21062306a36Sopenharmony_ci goto overflow2; 21162306a36Sopenharmony_ci linebuf[lx++] = ' '; 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci for (j = 0; j < len; j++) { 21462306a36Sopenharmony_ci if (linebuflen < lx + 2) 21562306a36Sopenharmony_ci goto overflow2; 21662306a36Sopenharmony_ci ch = ptr[j]; 21762306a36Sopenharmony_ci linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.'; 21862306a36Sopenharmony_ci } 21962306a36Sopenharmony_cinil: 22062306a36Sopenharmony_ci linebuf[lx] = '\0'; 22162306a36Sopenharmony_ci return lx; 22262306a36Sopenharmony_cioverflow2: 22362306a36Sopenharmony_ci linebuf[lx++] = '\0'; 22462306a36Sopenharmony_cioverflow1: 22562306a36Sopenharmony_ci return ascii ? ascii_column + len : (groupsize * 2 + 1) * ngroups - 1; 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ciEXPORT_SYMBOL(hex_dump_to_buffer); 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci#ifdef CONFIG_PRINTK 23062306a36Sopenharmony_ci/** 23162306a36Sopenharmony_ci * print_hex_dump - print a text hex dump to syslog for a binary blob of data 23262306a36Sopenharmony_ci * @level: kernel log level (e.g. KERN_DEBUG) 23362306a36Sopenharmony_ci * @prefix_str: string to prefix each line with; 23462306a36Sopenharmony_ci * caller supplies trailing spaces for alignment if desired 23562306a36Sopenharmony_ci * @prefix_type: controls whether prefix of an offset, address, or none 23662306a36Sopenharmony_ci * is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE) 23762306a36Sopenharmony_ci * @rowsize: number of bytes to print per line; must be 16 or 32 23862306a36Sopenharmony_ci * @groupsize: number of bytes to print at a time (1, 2, 4, 8; default = 1) 23962306a36Sopenharmony_ci * @buf: data blob to dump 24062306a36Sopenharmony_ci * @len: number of bytes in the @buf 24162306a36Sopenharmony_ci * @ascii: include ASCII after the hex output 24262306a36Sopenharmony_ci * 24362306a36Sopenharmony_ci * Given a buffer of u8 data, print_hex_dump() prints a hex + ASCII dump 24462306a36Sopenharmony_ci * to the kernel log at the specified kernel log level, with an optional 24562306a36Sopenharmony_ci * leading prefix. 24662306a36Sopenharmony_ci * 24762306a36Sopenharmony_ci * print_hex_dump() works on one "line" of output at a time, i.e., 24862306a36Sopenharmony_ci * 16 or 32 bytes of input data converted to hex + ASCII output. 24962306a36Sopenharmony_ci * print_hex_dump() iterates over the entire input @buf, breaking it into 25062306a36Sopenharmony_ci * "line size" chunks to format and print. 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci * E.g.: 25362306a36Sopenharmony_ci * print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS, 25462306a36Sopenharmony_ci * 16, 1, frame->data, frame->len, true); 25562306a36Sopenharmony_ci * 25662306a36Sopenharmony_ci * Example output using %DUMP_PREFIX_OFFSET and 1-byte mode: 25762306a36Sopenharmony_ci * 0009ab42: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @ABCDEFGHIJKLMNO 25862306a36Sopenharmony_ci * Example output using %DUMP_PREFIX_ADDRESS and 4-byte mode: 25962306a36Sopenharmony_ci * ffffffff88089af0: 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~. 26062306a36Sopenharmony_ci */ 26162306a36Sopenharmony_civoid print_hex_dump(const char *level, const char *prefix_str, int prefix_type, 26262306a36Sopenharmony_ci int rowsize, int groupsize, 26362306a36Sopenharmony_ci const void *buf, size_t len, bool ascii) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci const u8 *ptr = buf; 26662306a36Sopenharmony_ci int i, linelen, remaining = len; 26762306a36Sopenharmony_ci unsigned char linebuf[32 * 3 + 2 + 32 + 1]; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci if (rowsize != 16 && rowsize != 32) 27062306a36Sopenharmony_ci rowsize = 16; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci for (i = 0; i < len; i += rowsize) { 27362306a36Sopenharmony_ci linelen = min(remaining, rowsize); 27462306a36Sopenharmony_ci remaining -= rowsize; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, 27762306a36Sopenharmony_ci linebuf, sizeof(linebuf), ascii); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci switch (prefix_type) { 28062306a36Sopenharmony_ci case DUMP_PREFIX_ADDRESS: 28162306a36Sopenharmony_ci printk("%s%s%p: %s\n", 28262306a36Sopenharmony_ci level, prefix_str, ptr + i, linebuf); 28362306a36Sopenharmony_ci break; 28462306a36Sopenharmony_ci case DUMP_PREFIX_OFFSET: 28562306a36Sopenharmony_ci printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf); 28662306a36Sopenharmony_ci break; 28762306a36Sopenharmony_ci default: 28862306a36Sopenharmony_ci printk("%s%s%s\n", level, prefix_str, linebuf); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci} 29362306a36Sopenharmony_ciEXPORT_SYMBOL(print_hex_dump); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci#endif /* defined(CONFIG_PRINTK) */ 296