hexdump.c 7.55 KB
Newer Older
Randy Dunlap's avatar
Randy Dunlap committed
1 2 3 4 5 6 7 8 9 10 11 12
/*
 * 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 <linux/types.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
13
#include <linux/export.h>
Randy Dunlap's avatar
Randy Dunlap committed
14

15 16
const char hex_asc[] = "0123456789abcdef";
EXPORT_SYMBOL(hex_asc);
17 18
const char hex_asc_upper[] = "0123456789ABCDEF";
EXPORT_SYMBOL(hex_asc_upper);
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/**
 * 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.
 */
int hex_to_bin(char ch)
{
	if ((ch >= '0') && (ch <= '9'))
		return ch - '0';
	ch = tolower(ch);
	if ((ch >= 'a') && (ch <= 'f'))
		return ch - 'a' + 10;
	return -1;
}
EXPORT_SYMBOL(hex_to_bin);

38 39 40 41 42
/**
 * hex2bin - convert an ascii hexadecimal string to its binary representation
 * @dst: binary result
 * @src: ascii hexadecimal string
 * @count: result length
43 44
 *
 * Return 0 on success, -1 in case of bad input.
45
 */
46
int hex2bin(u8 *dst, const char *src, size_t count)
47 48
{
	while (count--) {
49 50 51 52 53 54 55
		int hi = hex_to_bin(*src++);
		int lo = hex_to_bin(*src++);

		if ((hi < 0) || (lo < 0))
			return -1;

		*dst++ = (hi << 4) | lo;
56
	}
57
	return 0;
58 59 60
}
EXPORT_SYMBOL(hex2bin);

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
/**
 * 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);

Randy Dunlap's avatar
Randy Dunlap committed
77 78 79 80
/**
 * 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
Randy Dunlap's avatar
Randy Dunlap committed
81 82
 * @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)
Randy Dunlap's avatar
Randy Dunlap committed
83 84
 * @linebuf: where to put the converted data
 * @linebuflen: total size of @linebuf, including space for terminating NUL
Randy Dunlap's avatar
Randy Dunlap committed
85
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
86 87
 *
 * hex_dump_to_buffer() works on one "line" of output at a time, i.e.,
Randy Dunlap's avatar
Randy Dunlap committed
88
 * 16 or 32 bytes of input data converted to hex + ASCII output.
Randy Dunlap's avatar
Randy Dunlap committed
89 90 91 92 93 94
 *
 * 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.:
Randy Dunlap's avatar
Randy Dunlap committed
95
 *   hex_dump_to_buffer(frame->data, frame->len, 16, 1,
96
 *			linebuf, sizeof(linebuf), true);
Randy Dunlap's avatar
Randy Dunlap committed
97 98
 *
 * example output buffer:
Randy Dunlap's avatar
Randy Dunlap committed
99
 * 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f  @ABCDEFGHIJKLMNO
Randy Dunlap's avatar
Randy Dunlap committed
100
 */
Randy Dunlap's avatar
Randy Dunlap committed
101 102 103
void hex_dump_to_buffer(const void *buf, size_t len, int rowsize,
			int groupsize, char *linebuf, size_t linebuflen,
			bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
104 105 106 107
{
	const u8 *ptr = buf;
	u8 ch;
	int j, lx = 0;
Randy Dunlap's avatar
Randy Dunlap committed
108
	int ascii_column;
Randy Dunlap's avatar
Randy Dunlap committed
109

Randy Dunlap's avatar
Randy Dunlap committed
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	if (!len)
		goto nil;
	if (len > rowsize)		/* limit to one line at a time */
		len = rowsize;
	if ((len % groupsize) != 0)	/* no mixed size output */
		groupsize = 1;

	switch (groupsize) {
	case 8: {
		const u64 *ptr8 = buf;
		int ngroups = len / groupsize;

		for (j = 0; j < ngroups; j++)
			lx += scnprintf(linebuf + lx, linebuflen - lx,
127 128
					"%s%16.16llx", j ? " " : "",
					(unsigned long long)*(ptr8 + j));
129
		ascii_column = rowsize * 2 + rowsize / 8 + 2;
Randy Dunlap's avatar
Randy Dunlap committed
130 131 132 133 134 135 136 137 138
		break;
	}

	case 4: {
		const u32 *ptr4 = buf;
		int ngroups = len / groupsize;

		for (j = 0; j < ngroups; j++)
			lx += scnprintf(linebuf + lx, linebuflen - lx,
139
					"%s%8.8x", j ? " " : "", *(ptr4 + j));
140
		ascii_column = rowsize * 2 + rowsize / 4 + 2;
Randy Dunlap's avatar
Randy Dunlap committed
141 142 143 144 145 146 147 148 149
		break;
	}

	case 2: {
		const u16 *ptr2 = buf;
		int ngroups = len / groupsize;

		for (j = 0; j < ngroups; j++)
			lx += scnprintf(linebuf + lx, linebuflen - lx,
150
					"%s%4.4x", j ? " " : "", *(ptr2 + j));
151
		ascii_column = rowsize * 2 + rowsize / 2 + 2;
Randy Dunlap's avatar
Randy Dunlap committed
152 153 154 155
		break;
	}

	default:
Li Zefan's avatar
Li Zefan committed
156
		for (j = 0; (j < len) && (lx + 3) <= linebuflen; j++) {
Randy Dunlap's avatar
Randy Dunlap committed
157
			ch = ptr[j];
158 159
			linebuf[lx++] = hex_asc_hi(ch);
			linebuf[lx++] = hex_asc_lo(ch);
Randy Dunlap's avatar
Randy Dunlap committed
160
			linebuf[lx++] = ' ';
Randy Dunlap's avatar
Randy Dunlap committed
161
		}
Li Zefan's avatar
Li Zefan committed
162 163 164
		if (j)
			lx--;

Randy Dunlap's avatar
Randy Dunlap committed
165 166
		ascii_column = 3 * rowsize + 2;
		break;
Randy Dunlap's avatar
Randy Dunlap committed
167
	}
Randy Dunlap's avatar
Randy Dunlap committed
168 169 170 171
	if (!ascii)
		goto nil;

	while (lx < (linebuflen - 1) && lx < (ascii_column - 1))
Randy Dunlap's avatar
Randy Dunlap committed
172
		linebuf[lx++] = ' ';
173 174 175 176
	for (j = 0; (j < len) && (lx + 2) < linebuflen; j++) {
		ch = ptr[j];
		linebuf[lx++] = (isascii(ch) && isprint(ch)) ? ch : '.';
	}
Randy Dunlap's avatar
Randy Dunlap committed
177
nil:
Randy Dunlap's avatar
Randy Dunlap committed
178 179 180 181
	linebuf[lx++] = '\0';
}
EXPORT_SYMBOL(hex_dump_to_buffer);

182
#ifdef CONFIG_PRINTK
Randy Dunlap's avatar
Randy Dunlap committed
183 184 185
/**
 * print_hex_dump - print a text hex dump to syslog for a binary blob of data
 * @level: kernel log level (e.g. KERN_DEBUG)
Randy Dunlap's avatar
Randy Dunlap committed
186 187
 * @prefix_str: string to prefix each line with;
 *  caller supplies trailing spaces for alignment if desired
Randy Dunlap's avatar
Randy Dunlap committed
188 189
 * @prefix_type: controls whether prefix of an offset, address, or none
 *  is printed (%DUMP_PREFIX_OFFSET, %DUMP_PREFIX_ADDRESS, %DUMP_PREFIX_NONE)
Randy Dunlap's avatar
Randy Dunlap committed
190 191
 * @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)
Randy Dunlap's avatar
Randy Dunlap committed
192 193
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
Randy Dunlap's avatar
Randy Dunlap committed
194
 * @ascii: include ASCII after the hex output
Randy Dunlap's avatar
Randy Dunlap committed
195 196 197 198 199
 *
 * 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.
 *
Randy Dunlap's avatar
Randy Dunlap committed
200 201 202 203 204
 * 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.
 *
Randy Dunlap's avatar
Randy Dunlap committed
205
 * E.g.:
Randy Dunlap's avatar
Randy Dunlap committed
206
 *   print_hex_dump(KERN_DEBUG, "raw data: ", DUMP_PREFIX_ADDRESS,
207
 *		    16, 1, frame->data, frame->len, true);
Randy Dunlap's avatar
Randy Dunlap committed
208
 *
Randy Dunlap's avatar
Randy Dunlap committed
209 210 211 212
 * 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{|}~.
Randy Dunlap's avatar
Randy Dunlap committed
213
 */
Randy Dunlap's avatar
Randy Dunlap committed
214
void print_hex_dump(const char *level, const char *prefix_str, int prefix_type,
215 216
		    int rowsize, int groupsize,
		    const void *buf, size_t len, bool ascii)
Randy Dunlap's avatar
Randy Dunlap committed
217
{
Artem Bityutskiy's avatar
Artem Bityutskiy committed
218
	const u8 *ptr = buf;
Randy Dunlap's avatar
Randy Dunlap committed
219
	int i, linelen, remaining = len;
220
	unsigned char linebuf[32 * 3 + 2 + 32 + 1];
Randy Dunlap's avatar
Randy Dunlap committed
221

Randy Dunlap's avatar
Randy Dunlap committed
222 223 224 225 226 227
	if (rowsize != 16 && rowsize != 32)
		rowsize = 16;

	for (i = 0; i < len; i += rowsize) {
		linelen = min(remaining, rowsize);
		remaining -= rowsize;
228

Randy Dunlap's avatar
Randy Dunlap committed
229
		hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize,
230
				   linebuf, sizeof(linebuf), ascii);
Randy Dunlap's avatar
Randy Dunlap committed
231 232 233

		switch (prefix_type) {
		case DUMP_PREFIX_ADDRESS:
234 235
			printk("%s%s%p: %s\n",
			       level, prefix_str, ptr + i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
236 237
			break;
		case DUMP_PREFIX_OFFSET:
Randy Dunlap's avatar
Randy Dunlap committed
238
			printk("%s%s%.8x: %s\n", level, prefix_str, i, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
239 240
			break;
		default:
Randy Dunlap's avatar
Randy Dunlap committed
241
			printk("%s%s%s\n", level, prefix_str, linebuf);
Randy Dunlap's avatar
Randy Dunlap committed
242 243 244 245 246
			break;
		}
	}
}
EXPORT_SYMBOL(print_hex_dump);
Randy Dunlap's avatar
Randy Dunlap committed
247

248
#if !defined(CONFIG_DYNAMIC_DEBUG)
Randy Dunlap's avatar
Randy Dunlap committed
249 250 251 252 253 254 255 256 257 258 259 260 261
/**
 * print_hex_dump_bytes - shorthand form of print_hex_dump() with default params
 * @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)
 * @buf: data blob to dump
 * @len: number of bytes in the @buf
 *
 * Calls print_hex_dump(), with log level of KERN_DEBUG,
 * rowsize of 16, groupsize of 1, and ASCII output included.
 */
void print_hex_dump_bytes(const char *prefix_str, int prefix_type,
262
			  const void *buf, size_t len)
Randy Dunlap's avatar
Randy Dunlap committed
263 264
{
	print_hex_dump(KERN_DEBUG, prefix_str, prefix_type, 16, 1,
265
		       buf, len, true);
Randy Dunlap's avatar
Randy Dunlap committed
266 267
}
EXPORT_SYMBOL(print_hex_dump_bytes);
268 269
#endif /* !defined(CONFIG_DYNAMIC_DEBUG) */
#endif /* defined(CONFIG_PRINTK) */