2019-06-04 11:11:34 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2007-05-11 09:22:39 +04:00
/*
* lib / hexdump . c
*/
# include <linux/types.h>
# include <linux/ctype.h>
2017-09-09 02:15:28 +03:00
# include <linux/errno.h>
2007-05-11 09:22:39 +04:00
# include <linux/kernel.h>
2020-10-16 06:10:21 +03:00
# include <linux/minmax.h>
2011-11-17 06:29:17 +04:00
# include <linux/export.h>
2015-07-18 02:24:04 +03:00
# include <asm/unaligned.h>
2007-05-11 09:22:39 +04:00
2008-05-15 03:05:49 +04:00
const char hex_asc [ ] = " 0123456789abcdef " ;
EXPORT_SYMBOL ( hex_asc ) ;
2013-09-13 21:37:12 +04:00
const char hex_asc_upper [ ] = " 0123456789ABCDEF " ;
EXPORT_SYMBOL ( hex_asc_upper ) ;
2008-05-15 03:05:49 +04:00
2010-05-25 01:33:23 +04:00
/**
* hex_to_bin - convert a hex digit to its real value
* @ ch : ascii character represents hex digit
*
* hex_to_bin ( ) converts one hex digit to its actual value or - 1 in case of bad
* input .
2022-04-25 15:07:48 +03:00
*
* This function is used to load cryptographic keys , so it is coded in such a
* way that there are no conditions or memory accesses that depend on data .
*
* Explanation of the logic :
* ( ch - ' 9 ' - 1 ) is negative if ch < = ' 9 '
* ( ' 0 ' - 1 - ch ) is negative if ch > = ' 0 '
* we " and " these two values , so the result is negative if ch is in the range
* ' 0 ' . . . ' 9 '
* we are only interested in the sign , so we do a shift " >> 8 " ; note that right
* shift of a negative value is implementation - defined , so we cast the
* value to ( unsigned ) before the shift - - - we have 0xffffff if ch is in
* the range ' 0 ' . . . ' 9 ' , 0 otherwise
* we " and " this value with ( ch - ' 0 ' + 1 ) - - - we have a value 1 . . . 10 if ch is
* in the range ' 0 ' . . . ' 9 ' , 0 otherwise
* we add this value to - 1 - - - we have a value 0 . . . 9 if ch is in the range ' 0 '
* . . . ' 9 ' , - 1 otherwise
* the next line is similar to the previous one , but we need to decode both
* uppercase and lowercase letters , so we use ( ch & 0xdf ) , which converts
* lowercase to uppercase
2010-05-25 01:33:23 +04:00
*/
2022-04-25 15:07:48 +03:00
int hex_to_bin ( unsigned char ch )
2010-05-25 01:33:23 +04:00
{
2022-04-25 15:07:48 +03:00
unsigned char cu = ch & 0xdf ;
return - 1 +
( ( ch - ' 0 ' + 1 ) & ( unsigned ) ( ( ch - ' 9 ' - 1 ) & ( ' 0 ' - 1 - ch ) ) > > 8 ) +
( ( cu - ' A ' + 11 ) & ( unsigned ) ( ( cu - ' F ' - 1 ) & ( ' A ' - 1 - cu ) ) > > 8 ) ;
2010-05-25 01:33:23 +04:00
}
EXPORT_SYMBOL ( hex_to_bin ) ;
2010-11-24 01:50:31 +03:00
/**
* hex2bin - convert an ascii hexadecimal string to its binary representation
* @ dst : binary result
* @ src : ascii hexadecimal string
* @ count : result length
2011-09-20 19:23:49 +04:00
*
2017-09-09 02:15:28 +03:00
* Return 0 on success , - EINVAL in case of bad input .
2010-11-24 01:50:31 +03:00
*/
2011-09-20 19:23:49 +04:00
int hex2bin ( u8 * dst , const char * src , size_t count )
2010-11-24 01:50:31 +03:00
{
while ( count - - ) {
2022-04-27 18:26:40 +03:00
int hi , lo ;
2011-09-20 19:23:49 +04:00
2022-04-27 18:26:40 +03:00
hi = hex_to_bin ( * src + + ) ;
if ( unlikely ( hi < 0 ) )
return - EINVAL ;
lo = hex_to_bin ( * src + + ) ;
if ( unlikely ( lo < 0 ) )
2017-09-09 02:15:28 +03:00
return - EINVAL ;
2011-09-20 19:23:49 +04:00
* dst + + = ( hi < < 4 ) | lo ;
2010-11-24 01:50:31 +03:00
}
2011-09-20 19:23:49 +04:00
return 0 ;
2010-11-24 01:50:31 +03:00
}
EXPORT_SYMBOL ( hex2bin ) ;
2014-09-16 20:36:01 +04:00
/**
* bin2hex - convert binary data to an ascii hexadecimal string
* @ dst : ascii hexadecimal result
* @ src : binary data
* @ count : binary data length
*/
char * bin2hex ( char * dst , const void * src , size_t count )
{
const unsigned char * _src = src ;
while ( count - - )
dst = hex_byte_pack ( dst , * _src + + ) ;
return dst ;
}
EXPORT_SYMBOL ( bin2hex ) ;
2007-05-11 09:22:39 +04:00
/**
* 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
2007-06-09 00:47:04 +04:00
* @ 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 )
2007-05-11 09:22:39 +04:00
* @ linebuf : where to put the converted data
* @ linebuflen : total size of @ linebuf , including space for terminating NUL
2007-06-09 00:47:04 +04:00
* @ ascii : include ASCII after the hex output
2007-05-11 09:22:39 +04:00
*
* hex_dump_to_buffer ( ) works on one " line " of output at a time , i . e . ,
2007-06-09 00:47:04 +04:00
* 16 or 32 bytes of input data converted to hex + ASCII output .
2007-05-11 09:22:39 +04:00
*
* 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 . :
2007-06-09 00:47:04 +04:00
* hex_dump_to_buffer ( frame - > data , frame - > len , 16 , 1 ,
2010-05-25 01:33:22 +04:00
* linebuf , sizeof ( linebuf ) , true ) ;
2007-05-11 09:22:39 +04:00
*
* example output buffer :
2007-06-09 00:47:04 +04:00
* 40 41 42 43 44 45 46 47 48 49 4 a 4 b 4 c 4 d 4 e 4f @ ABCDEFGHIJKLMNO
2015-02-13 02:02:29 +03:00
*
* 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 .
2007-05-11 09:22:39 +04:00
*/
2015-02-13 02:02:29 +03:00
int hex_dump_to_buffer ( const void * buf , size_t len , int rowsize , int groupsize ,
char * linebuf , size_t linebuflen , bool ascii )
2007-05-11 09:22:39 +04:00
{
const u8 * ptr = buf ;
2015-02-13 02:02:26 +03:00
int ngroups ;
2007-05-11 09:22:39 +04:00
u8 ch ;
int j , lx = 0 ;
2007-06-09 00:47:04 +04:00
int ascii_column ;
2015-02-13 02:02:29 +03:00
int ret ;
2007-05-11 09:22:39 +04:00
2007-06-09 00:47:04 +04:00
if ( rowsize ! = 16 & & rowsize ! = 32 )
rowsize = 16 ;
if ( len > rowsize ) /* limit to one line at a time */
len = rowsize ;
2015-02-13 02:02:26 +03:00
if ( ! is_power_of_2 ( groupsize ) | | groupsize > 8 )
groupsize = 1 ;
2007-06-09 00:47:04 +04:00
if ( ( len % groupsize ) ! = 0 ) /* no mixed size output */
groupsize = 1 ;
2015-02-13 02:02:26 +03:00
ngroups = len / groupsize ;
ascii_column = rowsize * 2 + rowsize / groupsize + 1 ;
2015-02-13 02:02:29 +03:00
if ( ! linebuflen )
goto overflow1 ;
if ( ! len )
goto nil ;
2015-02-13 02:02:26 +03:00
if ( groupsize = = 8 ) {
2007-06-09 00:47:04 +04:00
const u64 * ptr8 = buf ;
2015-02-13 02:02:29 +03:00
for ( j = 0 ; j < ngroups ; j + + ) {
ret = snprintf ( linebuf + lx , linebuflen - lx ,
" %s%16.16llx " , j ? " " : " " ,
2015-07-18 02:24:04 +03:00
get_unaligned ( ptr8 + j ) ) ;
2015-02-13 02:02:29 +03:00
if ( ret > = linebuflen - lx )
goto overflow1 ;
lx + = ret ;
}
2015-02-13 02:02:26 +03:00
} else if ( groupsize = = 4 ) {
2007-06-09 00:47:04 +04:00
const u32 * ptr4 = buf ;
2015-02-13 02:02:29 +03:00
for ( j = 0 ; j < ngroups ; j + + ) {
ret = snprintf ( linebuf + lx , linebuflen - lx ,
" %s%8.8x " , j ? " " : " " ,
2015-07-18 02:24:04 +03:00
get_unaligned ( ptr4 + j ) ) ;
2015-02-13 02:02:29 +03:00
if ( ret > = linebuflen - lx )
goto overflow1 ;
lx + = ret ;
}
2015-02-13 02:02:26 +03:00
} else if ( groupsize = = 2 ) {
2007-06-09 00:47:04 +04:00
const u16 * ptr2 = buf ;
2015-02-13 02:02:29 +03:00
for ( j = 0 ; j < ngroups ; j + + ) {
ret = snprintf ( linebuf + lx , linebuflen - lx ,
" %s%4.4x " , j ? " " : " " ,
2015-07-18 02:24:04 +03:00
get_unaligned ( ptr2 + j ) ) ;
2015-02-13 02:02:29 +03:00
if ( ret > = linebuflen - lx )
goto overflow1 ;
lx + = ret ;
}
2015-02-13 02:02:26 +03:00
} else {
2015-02-13 02:02:29 +03:00
for ( j = 0 ; j < len ; j + + ) {
2015-11-07 03:31:31 +03:00
if ( linebuflen < lx + 2 )
2015-02-13 02:02:29 +03:00
goto overflow2 ;
2007-06-09 00:47:04 +04:00
ch = ptr [ j ] ;
2008-05-15 03:05:49 +04:00
linebuf [ lx + + ] = hex_asc_hi ( ch ) ;
2015-11-07 03:31:31 +03:00
if ( linebuflen < lx + 2 )
goto overflow2 ;
2008-05-15 03:05:49 +04:00
linebuf [ lx + + ] = hex_asc_lo ( ch ) ;
2015-11-07 03:31:31 +03:00
if ( linebuflen < lx + 2 )
goto overflow2 ;
2007-05-11 09:22:39 +04:00
linebuf [ lx + + ] = ' ' ;
2007-06-09 00:47:04 +04:00
}
hexdump: remove the trailing space
For example:
hex_dump_to_buffer("AB", 2, 16, 1, buf, 100, 0);
pr_info("[%s]\n", buf);
I'd expect the output to be "[41 42]", but actually it's "[41 42 ]"
This patch also makes the required buf to be minimum. To print the hex
format of "AB", a buf with size 6 should be sufficient, but
hex_dump_to_buffer() required at least 8.
Signed-off-by: Li Zefan <lizf@cn.fujitsu.com>
Acked-by: Randy Dunlap <randy.dunlap@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2009-06-17 02:33:45 +04:00
if ( j )
lx - - ;
2007-05-11 09:22:39 +04:00
}
2007-06-09 00:47:04 +04:00
if ( ! ascii )
goto nil ;
2015-02-13 02:02:29 +03:00
while ( lx < ascii_column ) {
if ( linebuflen < lx + 2 )
goto overflow2 ;
2007-05-11 09:22:39 +04:00
linebuf [ lx + + ] = ' ' ;
2015-02-13 02:02:29 +03:00
}
for ( j = 0 ; j < len ; j + + ) {
if ( linebuflen < lx + 2 )
goto overflow2 ;
2010-05-25 01:33:22 +04:00
ch = ptr [ j ] ;
linebuf [ lx + + ] = ( isascii ( ch ) & & isprint ( ch ) ) ? ch : ' . ' ;
}
2007-06-09 00:47:04 +04:00
nil :
2015-02-13 02:02:29 +03:00
linebuf [ lx ] = ' \0 ' ;
return lx ;
overflow2 :
2007-05-11 09:22:39 +04:00
linebuf [ lx + + ] = ' \0 ' ;
2015-02-13 02:02:29 +03:00
overflow1 :
return ascii ? ascii_column + len : ( groupsize * 2 + 1 ) * ngroups - 1 ;
2007-05-11 09:22:39 +04:00
}
EXPORT_SYMBOL ( hex_dump_to_buffer ) ;
2011-01-13 03:59:47 +03:00
# ifdef CONFIG_PRINTK
2007-05-11 09:22:39 +04:00
/**
* print_hex_dump - print a text hex dump to syslog for a binary blob of data
* @ level : kernel log level ( e . g . KERN_DEBUG )
2007-06-09 00:47:04 +04:00
* @ prefix_str : string to prefix each line with ;
* caller supplies trailing spaces for alignment if desired
2007-05-11 09:22:39 +04:00
* @ prefix_type : controls whether prefix of an offset , address , or none
* is printed ( % DUMP_PREFIX_OFFSET , % DUMP_PREFIX_ADDRESS , % DUMP_PREFIX_NONE )
2007-06-09 00:47:04 +04:00
* @ 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 )
2007-05-11 09:22:39 +04:00
* @ buf : data blob to dump
* @ len : number of bytes in the @ buf
2007-06-09 00:47:04 +04:00
* @ ascii : include ASCII after the hex output
2007-05-11 09:22:39 +04:00
*
* 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 .
*
2007-06-09 00:47:04 +04:00
* 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 .
*
2007-05-11 09:22:39 +04:00
* E . g . :
2007-06-09 00:47:04 +04:00
* print_hex_dump ( KERN_DEBUG , " raw data: " , DUMP_PREFIX_ADDRESS ,
2010-05-25 01:33:22 +04:00
* 16 , 1 , frame - > data , frame - > len , true ) ;
2007-05-11 09:22:39 +04:00
*
2007-06-09 00:47:04 +04:00
* Example output using % DUMP_PREFIX_OFFSET and 1 - byte mode :
* 000 9 ab42 : 40 41 42 43 44 45 46 47 48 49 4 a 4 b 4 c 4 d 4 e 4f @ ABCDEFGHIJKLMNO
* Example output using % DUMP_PREFIX_ADDRESS and 4 - byte mode :
* ffffffff88089af0 : 73727170 77767574 7 b7a7978 7f 7e7 d7c pqrstuvwxyz { | } ~ .
2007-05-11 09:22:39 +04:00
*/
2007-06-09 00:47:04 +04:00
void print_hex_dump ( const char * level , const char * prefix_str , int prefix_type ,
2010-05-25 01:33:22 +04:00
int rowsize , int groupsize ,
const void * buf , size_t len , bool ascii )
2007-05-11 09:22:39 +04:00
{
2007-08-08 00:43:14 +04:00
const u8 * ptr = buf ;
2007-05-11 09:22:39 +04:00
int i , linelen , remaining = len ;
2010-05-25 01:33:22 +04:00
unsigned char linebuf [ 32 * 3 + 2 + 32 + 1 ] ;
2007-05-11 09:22:39 +04:00
2007-06-09 00:47:04 +04:00
if ( rowsize ! = 16 & & rowsize ! = 32 )
rowsize = 16 ;
for ( i = 0 ; i < len ; i + = rowsize ) {
linelen = min ( remaining , rowsize ) ;
remaining - = rowsize ;
2010-05-25 01:33:22 +04:00
2007-06-09 00:47:04 +04:00
hex_dump_to_buffer ( ptr + i , linelen , rowsize , groupsize ,
2010-05-25 01:33:22 +04:00
linebuf , sizeof ( linebuf ) , ascii ) ;
2007-05-11 09:22:39 +04:00
switch ( prefix_type ) {
case DUMP_PREFIX_ADDRESS :
2010-05-25 01:33:22 +04:00
printk ( " %s%s%p: %s \n " ,
level , prefix_str , ptr + i , linebuf ) ;
2007-05-11 09:22:39 +04:00
break ;
case DUMP_PREFIX_OFFSET :
2007-06-09 00:47:04 +04:00
printk ( " %s%s%.8x: %s \n " , level , prefix_str , i , linebuf ) ;
2007-05-11 09:22:39 +04:00
break ;
default :
2007-06-09 00:47:04 +04:00
printk ( " %s%s%s \n " , level , prefix_str , linebuf ) ;
2007-05-11 09:22:39 +04:00
break ;
}
}
}
EXPORT_SYMBOL ( print_hex_dump ) ;
2007-06-09 00:47:04 +04:00
2012-12-06 01:48:27 +04:00
# endif /* defined(CONFIG_PRINTK) */